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)
54 DECLARE_OPEN(clone
, false)
55 DECLARE_OPEN(wall
, false)
56 DECLARE_OPEN(panoramix
, false)
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 ) \
68 set_category( CAT_VIDEO
)
69 set_subcategory( SUBCAT_VIDEO_VFILTER
)
71 DECLARE_MODULE(magnify
)
74 DECLARE_MODULE(puzzle
)
86 DECLARE_MODULE(panoramix
)
92 /*****************************************************************************
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 );
103 vout_thread_t
**pp_vout
;
108 filter_chain_t
*p_chain
;
109 video_splitter_t
*p_splitter
;
111 vlc_mouse_t
*p_mouse_src
;
116 static int FilterAllocationInit ( filter_t
*, void * );
117 static void FilterAllocationClean( filter_t
* );
120 static int FullscreenEventUp( vlc_object_t
*, char const *,
121 vlc_value_t
, vlc_value_t
, void * );
122 static int FullscreenEventDown( vlc_object_t
*, char const *,
123 vlc_value_t
, vlc_value_t
, void * );
124 static int SplitterPictureNew( video_splitter_t
*, picture_t
*pp_picture
[] );
125 static void SplitterPictureDel( video_splitter_t
*, picture_t
*pp_picture
[] );
128 static int MouseEvent( vlc_object_t
*, char const *,
129 vlc_value_t
, vlc_value_t
, void * );
130 static void VoutsClean( vout_thread_t
*p_vout
, int i_count
);
133 * Open our wrapper instance.
135 static int Open( vlc_object_t
*p_this
, const char *psz_name
, bool b_filter
)
137 vout_thread_t
*p_vout
= (vout_thread_t
*)p_this
;
140 msg_Err( p_vout
, "Opening video %s wrapper for %s",
141 b_filter
? "filter" : "splitter", psz_name
);
145 es_format_Init( &fmt
, VIDEO_ES
, p_vout
->render
.i_chroma
);
146 video_format_Setup( &fmt
.video
, p_vout
->render
.i_chroma
,
147 p_vout
->render
.i_width
, p_vout
->render
.i_height
,
148 p_vout
->render
.i_aspect
* p_vout
->render
.i_height
,
149 VOUT_ASPECT_FACTOR
* p_vout
->render
.i_width
);
151 /* Try to open our real module */
152 filter_chain_t
*p_chain
= NULL
;
153 video_splitter_t
*p_splitter
= NULL
;
156 p_chain
= filter_chain_New( p_vout
, "video filter2", false,
157 FilterAllocationInit
, FilterAllocationClean
, p_vout
);
161 filter_chain_Reset( p_chain
, &fmt
, &fmt
);
164 filter_chain_AppendFilter( p_chain
, psz_name
, p_vout
->p_cfg
, &fmt
, &fmt
);
168 msg_Err( p_vout
, "Failed to open filter '%s'", psz_name
);
169 filter_chain_Delete( p_chain
);
175 p_splitter
= video_splitter_New( VLC_OBJECT(p_vout
), psz_name
, &fmt
.video
);
178 msg_Err( p_vout
, "Failed to open splitter '%s'", psz_name
);
181 assert( p_splitter
->i_output
> 0 );
183 p_splitter
->p_owner
= (video_splitter_owner_t
*)p_vout
;
184 p_splitter
->pf_picture_new
= SplitterPictureNew
;
185 p_splitter
->pf_picture_del
= SplitterPictureDel
;
189 p_vout
->p_sys
= p_sys
= malloc( sizeof(*p_sys
) );
193 p_sys
->i_vout
= p_chain
? 1 : p_splitter
->i_output
;
194 p_sys
->pp_vout
= calloc( p_sys
->i_vout
, sizeof(*p_sys
->pp_vout
) );;
195 p_sys
->p_mouse_src
= calloc( p_sys
->i_vout
, sizeof(*p_sys
->p_mouse_src
) );
198 vlc_mutex_init( &p_sys
->lock
);
199 p_sys
->p_chain
= p_chain
;
200 p_sys
->p_splitter
= p_splitter
;
201 vlc_mouse_Init( &p_sys
->mouse
);
202 for( int i
= 0; i
< p_sys
->i_vout
; i
++ )
203 vlc_mouse_Init( &p_sys
->p_mouse_src
[i
] );
205 p_vout
->pf_init
= Init
;
206 p_vout
->pf_end
= End
;
207 p_vout
->pf_manage
= NULL
;
208 p_vout
->pf_render
= Render
;
209 p_vout
->pf_display
= NULL
;
210 p_vout
->pf_control
= Control
;
216 filter_chain_Delete( p_chain
);
218 video_splitter_Delete( p_splitter
);
223 * Close our wrapper instance
225 static void Close( vlc_object_t
*p_this
)
227 vout_thread_t
*p_vout
= (vout_thread_t
*)p_this
;
228 vout_sys_t
*p_sys
= p_vout
->p_sys
;
231 filter_chain_Delete( p_sys
->p_chain
);
232 if( p_sys
->p_splitter
)
233 video_splitter_Delete( p_sys
->p_splitter
);
235 vlc_mutex_destroy( &p_sys
->lock
);
236 es_format_Clean( &p_sys
->fmt
);
237 free( p_sys
->p_mouse_src
);
238 free( p_sys
->pp_vout
);
240 free( p_vout
->p_sys
);
244 * Initialise our wrapper
246 static int Init( vout_thread_t
*p_vout
)
248 vout_sys_t
*p_sys
= p_vout
->p_sys
;
250 assert( p_vout
->render
.i_chroma
== p_sys
->fmt
.video
.i_chroma
&&
251 p_vout
->render
.i_width
== p_sys
->fmt
.video
.i_width
&&
252 p_vout
->render
.i_height
== p_sys
->fmt
.video
.i_height
);
254 /* Initialize the output structure */
255 I_OUTPUTPICTURES
= 0;
256 p_vout
->output
.i_chroma
= p_vout
->render
.i_chroma
;
257 p_vout
->output
.i_width
= p_vout
->render
.i_width
;
258 p_vout
->output
.i_height
= p_vout
->render
.i_height
;
259 p_vout
->output
.i_aspect
= p_vout
->render
.i_aspect
;
261 p_vout
->fmt_out
= p_vout
->fmt_in
;
263 /* Try to open the real video output */
264 msg_Dbg( p_vout
, "spawning the real video output(s)" );
266 video_format_t fmt
= p_vout
->fmt_out
;
270 p_sys
->pp_vout
[0] = vout_Create( p_vout
, &fmt
);
271 if( !p_sys
->pp_vout
[0] )
273 msg_Err( p_vout
, "cannot open vout, aborting" );
276 vout_filter_AddChild( p_vout
, p_sys
->pp_vout
[0], MouseEvent
);
280 video_splitter_t
*p_splitter
= p_sys
->p_splitter
;
283 const int i_org_align
= var_CreateGetInteger( p_vout
, "align" );
284 const int i_org_x
= var_CreateGetInteger( p_vout
, "video-x" );
285 const int i_org_y
= var_CreateGetInteger( p_vout
, "video-y" );
286 const char *psz_org_vout
= var_CreateGetNonEmptyString( p_vout
, "vout" );
289 for( int i
= 0; i
< p_splitter
->i_output
; i
++ )
291 const video_splitter_output_t
*p_cfg
= &p_splitter
->p_output
[i
];
294 var_SetInteger( p_vout
, "align", p_cfg
->window
.i_align
);
296 var_SetInteger( p_vout
, "video-x", i_org_x
+ p_cfg
->window
.i_x
);
297 var_SetInteger( p_vout
, "video-y", i_org_y
+ p_cfg
->window
.i_y
);
299 if( p_cfg
->psz_module
)
300 var_SetString( p_vout
, "vout", p_cfg
->psz_module
);
303 video_format_t fmt
= p_cfg
->fmt
;
304 p_sys
->pp_vout
[i
] = vout_Create( p_vout
, &fmt
);
305 if( !p_sys
->pp_vout
[i
] )
307 msg_Err( p_vout
, "cannot open vout, aborting" );
308 VoutsClean( p_vout
, i
);
313 /* Attach once pp_vout is completly field to avoid race conditions */
314 for( int i
= 0; i
< p_splitter
->i_output
; i
++ )
315 vout_filter_SetupChild( p_vout
, p_sys
->pp_vout
[i
],
317 FullscreenEventUp
, FullscreenEventDown
, true );
318 /* Restore settings */
319 var_SetInteger( p_vout
, "align", i_org_align
);
320 var_SetInteger( p_vout
, "video-x", i_org_x
);
321 var_SetInteger( p_vout
, "video-y", i_org_y
);
322 var_SetString( p_vout
, "vout", psz_org_vout
? psz_org_vout
: "" );
325 vout_filter_AllocateDirectBuffers( p_vout
, VOUT_MAX_PICTURES
);
331 * Clean up our wrapper
333 static void End( vout_thread_t
*p_vout
)
335 vout_sys_t
*p_sys
= p_vout
->p_sys
;
337 VoutsClean( p_vout
, p_sys
->i_vout
);
339 vout_filter_ReleaseDirectBuffers( p_vout
);
343 * Control the real vout
345 static int Control( vout_thread_t
*p_vout
, int i_query
, va_list args
)
347 vout_sys_t
*p_sys
= p_vout
->p_sys
;
348 int i_ret
= VLC_SUCCESS
;
350 for( int i
= 0; i
< p_sys
->i_vout
; i
++ )
351 i_ret
= vout_vaControl( p_sys
->pp_vout
[i
], i_query
, args
);
358 static void Render( vout_thread_t
*p_vout
, picture_t
*p_src
)
360 vout_sys_t
*p_sys
= p_vout
->p_sys
;
362 vlc_mutex_lock( &p_sys
->lock
);
364 picture_t
*pp_dst
[p_sys
->i_vout
];
368 pp_dst
[0] = filter_chain_VideoFilter( p_sys
->p_chain
, p_src
);
372 if( video_splitter_Filter( p_sys
->p_splitter
, pp_dst
, p_src
) )
374 for( int i
= 0; i
< p_sys
->i_vout
; i
++ )
378 for( int i
= 0; i
< p_sys
->i_vout
; i
++ )
380 picture_t
*p_dst
= pp_dst
[i
];
382 vout_DisplayPicture( p_sys
->pp_vout
[i
], p_dst
);
385 vlc_mutex_unlock( &p_sys
->lock
);
389 static void VoutsClean( vout_thread_t
*p_vout
, int i_count
)
391 vout_sys_t
*p_sys
= p_vout
->p_sys
;
393 /* Detach all vouts before destroying them */
394 for( int i
= 0; i
< i_count
; i
++ )
397 vout_filter_DelChild( p_vout
, p_sys
->pp_vout
[i
], MouseEvent
);
399 vout_filter_SetupChild( p_vout
, p_sys
->pp_vout
[i
],
401 FullscreenEventUp
, FullscreenEventDown
, false );
404 for( int i
= 0; i
< i_count
; i
++ )
405 vout_CloseAndRelease( p_sys
->pp_vout
[i
] );
407 static int VoutsNewPicture( vout_thread_t
*p_vout
, picture_t
*pp_dst
[] )
409 vout_sys_t
*p_sys
= p_vout
->p_sys
;
411 for( int i
= 0; i
< p_sys
->i_vout
; i
++ )
413 picture_t
*p_picture
= NULL
;
416 p_picture
= vout_CreatePicture( p_sys
->pp_vout
[i
], 0, 0, 0 );
420 if( !vlc_object_alive( p_vout
) || p_vout
->b_error
)
422 msleep( VOUT_OUTMEM_SLEEP
);
424 /* FIXME what to do with the allocated picture ? */
428 pp_dst
[i
] = p_picture
;
434 * Callback for mouse events
436 static int MouseEvent( vlc_object_t
*p_this
, char const *psz_var
,
437 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
439 VLC_UNUSED(psz_var
); VLC_UNUSED(oldval
); VLC_UNUSED(newval
);
440 vout_thread_t
*p_vout
= p_data
;
441 vout_sys_t
*p_sys
= p_vout
->p_sys
;
444 for( i_index
= 0; i_index
< p_sys
->i_vout
; i_index
++ )
446 if( p_this
== VLC_OBJECT(p_sys
->pp_vout
[i_index
]) )
449 if( i_index
>= p_sys
->i_vout
)
451 msg_Err( p_vout
, "Failed to find vout source in MouseEvent" );
455 vout_thread_t
*p_vout_src
= p_sys
->pp_vout
[i_index
];
458 vlc_mouse_Init( &m
);
459 m
.i_x
= var_GetInteger( p_vout_src
, "mouse-x" );
460 m
.i_y
= var_GetInteger( p_vout_src
, "mouse-y" );
461 m
.i_pressed
= var_GetInteger( p_vout_src
, "mouse-button-down" );
463 vlc_mutex_lock( &p_sys
->lock
);
466 vlc_mouse_t omouse
= p_sys
->mouse
;
471 i_ret
= filter_chain_MouseFilter( p_sys
->p_chain
, &nmouse
, &m
);
475 vlc_mouse_t
*p_mouse_src
= &p_sys
->p_mouse_src
[i_index
];
477 i_ret
= video_splitter_Mouse( p_sys
->p_splitter
, &nmouse
, i_index
, p_mouse_src
, &m
);
482 p_sys
->mouse
= nmouse
;
483 vlc_mutex_unlock( &p_sys
->lock
);
488 if( vlc_mouse_HasMoved( &omouse
, &nmouse
) )
490 var_SetInteger( p_vout
, "mouse-x", nmouse
.i_x
);
491 var_SetInteger( p_vout
, "mouse-y", nmouse
.i_y
);
492 var_SetBool( p_vout
, "mouse-moved", true );
494 if( vlc_mouse_HasButton( &omouse
, &nmouse
) )
496 var_SetInteger( p_vout
, "mouse-button-down", nmouse
.i_pressed
);
497 if( vlc_mouse_HasPressed( &omouse
, &nmouse
, MOUSE_BUTTON_LEFT
) )
498 var_SetBool( p_vout
, "mouse-clicked", true );
500 if( m
.b_double_click
)
502 /* Nothing with current API */
503 msg_Warn( p_vout
, "Ignoring double click" );
508 /* -- Filter callbacks -- */
510 static picture_t
*VideoBufferNew( filter_t
*p_filter
)
512 vout_thread_t
*p_vout
= (vout_thread_t
*)p_filter
->p_owner
;
514 picture_t
*pp_picture
[1];
515 if( VoutsNewPicture( p_vout
, pp_picture
) )
517 return pp_picture
[0];
519 static void VideoBufferDelete( filter_t
*p_filter
, picture_t
*p_picture
)
521 VLC_UNUSED(p_filter
); VLC_UNUSED(p_picture
);
522 /* FIXME is there anything to do ? */
525 static int FilterAllocationInit( filter_t
*p_filter
, void *p_data
)
527 VLC_UNUSED( p_data
);
529 p_filter
->pf_video_buffer_new
= VideoBufferNew
;
530 p_filter
->pf_video_buffer_del
= VideoBufferDelete
;
531 p_filter
->p_owner
= p_data
;
535 static void FilterAllocationClean( filter_t
*p_filter
)
537 p_filter
->pf_video_buffer_new
= NULL
;
538 p_filter
->pf_video_buffer_del
= NULL
;
541 /* -- Splitter callbacks -- */
544 * Forward fullscreen event to/from the childrens.
546 * FIXME probably unsafe (pp_vout[] content)
548 static bool IsFullscreenActive( vout_thread_t
*p_vout
)
550 vout_sys_t
*p_sys
= p_vout
->p_sys
;
551 for( int i
= 0; i
< p_sys
->i_vout
; i
++ )
553 if( var_GetBool( p_sys
->pp_vout
[i
], "fullscreen" ) )
558 static int FullscreenEventUp( vlc_object_t
*p_this
, char const *psz_var
,
559 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
561 vout_thread_t
*p_vout
= p_data
;
562 VLC_UNUSED(oldval
); VLC_UNUSED(p_this
); VLC_UNUSED(psz_var
); VLC_UNUSED(newval
);
564 const bool b_fullscreen
= IsFullscreenActive( p_vout
);
565 if( !var_GetBool( p_vout
, "fullscreen" ) != !b_fullscreen
)
566 return var_SetBool( p_vout
, "fullscreen", b_fullscreen
);
569 static int FullscreenEventDown( vlc_object_t
*p_this
, char const *psz_var
,
570 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
572 vout_thread_t
*p_vout
= (vout_thread_t
*)p_this
;
573 vout_sys_t
*p_sys
= p_vout
->p_sys
;
574 VLC_UNUSED(oldval
); VLC_UNUSED(p_data
); VLC_UNUSED(psz_var
);
576 const bool b_fullscreen
= IsFullscreenActive( p_vout
);
577 if( !b_fullscreen
!= !newval
.b_bool
)
579 for( int i
= 0; i
< p_sys
->i_vout
; i
++ )
581 vout_thread_t
*p_child
= p_sys
->pp_vout
[i
];
582 if( !var_GetBool( p_child
, "fullscreen" ) != !newval
.b_bool
)
584 var_SetBool( p_child
, "fullscreen", newval
.b_bool
);
593 static int SplitterPictureNew( video_splitter_t
*p_splitter
, picture_t
*pp_picture
[] )
595 vout_thread_t
*p_vout
= (vout_thread_t
*)p_splitter
->p_owner
;
597 return VoutsNewPicture( p_vout
, pp_picture
);
599 static void SplitterPictureDel( video_splitter_t
*p_splitter
, picture_t
*pp_picture
[] )
601 VLC_UNUSED(p_splitter
); VLC_UNUSED(pp_picture
);
602 /* FIXME is there anything to do ? */