8147d1638014ff9983f1fde2402041da28307211
[vlc.git] / modules / video_filter / logo.c
1 /*****************************************************************************
2  * logo.c : logo video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *          Simon Latapie <garf@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #include <vlc/vlc.h>
30 #include <vlc_vout.h>
31
32 #include "vlc_filter.h"
33 #include "filter_common.h"
34 #include "vlc_image.h"
35 #include "vlc_osd.h"
36
37 #ifdef LoadImage
38 #   undef LoadImage
39 #endif
40
41 /*****************************************************************************
42  * Local prototypes
43  *****************************************************************************/
44 static int  Create    ( vlc_object_t * );
45 static void Destroy   ( vlc_object_t * );
46
47 static int  Init      ( vout_thread_t * );
48 static void End       ( vout_thread_t * );
49 static void Render    ( vout_thread_t *, picture_t * );
50
51 static int  SendEvents( vlc_object_t *, char const *,
52                         vlc_value_t, vlc_value_t, void * );
53 static int  MouseEvent( vlc_object_t *, char const *,
54                         vlc_value_t , vlc_value_t , void * );
55 static int  Control   ( vout_thread_t *, int, va_list );
56
57 static int  CreateFilter ( vlc_object_t * );
58 static void DestroyFilter( vlc_object_t * );
59
60 static int LogoCallback( vlc_object_t *, char const *,
61                          vlc_value_t, vlc_value_t, void * );
62
63 /*****************************************************************************
64  * Module descriptor
65  *****************************************************************************/
66 #define FILE_TEXT N_("Logo filenames")
67 #define FILE_LONGTEXT N_("Full path of the image files to use. Format is " \
68 "<image>[,<delay in ms>[,<alpha>]][;<image>[,<delay>[,<alpha>]]][;...]. " \
69 "If you only have one file, simply enter its filename.")
70 #define REPEAT_TEXT N_("Logo animation # of loops")
71 #define REPEAT_LONGTEXT N_("Number of loops for the logo animation." \
72         "-1 = continuous, 0 = disabled")
73 #define DELAY_TEXT N_("Logo individual image time in ms")
74 #define DELAY_LONGTEXT N_("Individual image display time of 0 - 60000 ms.")
75
76 #define POSX_TEXT N_("X coordinate")
77 #define POSX_LONGTEXT N_("X coordinate of the logo. You can move the logo " \
78                 "by left-clicking it." )
79 #define POSY_TEXT N_("Y coordinate")
80 #define POSY_LONGTEXT N_("Y coordinate of the logo. You can move the logo " \
81                 "by left-clicking it." )
82 #define TRANS_TEXT N_("Transparency of the logo")
83 #define TRANS_LONGTEXT N_("Logo transparency value " \
84   "(from 0 for full transparency to 255 for full opacity)." )
85 #define POS_TEXT N_("Logo position")
86 #define POS_LONGTEXT N_( \
87   "Enforce the logo position on the video " \
88   "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
89   "also use combinations of these values, eg 6 = top-right).")
90
91 #define CFG_PREFIX "logo-"
92
93 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
94 static const char *ppsz_pos_descriptions[] =
95 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
96   N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
97
98 vlc_module_begin();
99     set_description( _("Logo video filter") );
100     set_capability( "video filter", 0 );
101     set_shortname( _("Logo overlay") );
102     set_category( CAT_VIDEO );
103     set_subcategory( SUBCAT_VIDEO_SUBPIC );
104     add_shortcut( "logo" );
105     set_callbacks( Create, Destroy );
106
107     add_file( CFG_PREFIX "file", NULL, NULL, FILE_TEXT, FILE_LONGTEXT, VLC_FALSE );
108     add_integer( CFG_PREFIX "x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
109     add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
110     /* default to 1000 ms per image, continuously cycle through them */
111     add_integer( CFG_PREFIX "delay", 1000, NULL, DELAY_TEXT, DELAY_LONGTEXT, VLC_TRUE );
112     add_integer( CFG_PREFIX "repeat", -1, NULL, REPEAT_TEXT, REPEAT_LONGTEXT, VLC_TRUE );
113     add_integer_with_range( CFG_PREFIX "transparency", 255, 0, 255, NULL,
114         TRANS_TEXT, TRANS_LONGTEXT, VLC_FALSE );
115     add_integer( CFG_PREFIX "position", 6, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
116         change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
117
118     /* subpicture filter submodule */
119     add_submodule();
120     set_capability( "sub filter", 0 );
121     set_callbacks( CreateFilter, DestroyFilter );
122     set_description( _("Logo sub filter") );
123 vlc_module_end();
124
125 static const char *ppsz_filter_options[] = {
126     "file", "x", "y", "delay", "repeat", "transparency", "position", NULL
127 };
128
129 /*****************************************************************************
130  * Structure to hold the set of individual logo image names, times,
131  * transparencies
132  ****************************************************************************/
133 typedef struct
134 {
135     char *psz_file;    /* candidate for deletion -- not needed */
136     int i_delay;       /* -1 means use default delay */
137     int i_alpha;       /* -1 means use default alpha */
138     picture_t *p_pic;
139
140 } logo_t;
141
142 /*****************************************************************************
143  * Logo list structure. Common to both the vout and sub picture filter
144  ****************************************************************************/
145 typedef struct
146 {
147     logo_t *p_logo;         /* the parsing's result */
148     unsigned int i_count;   /* the number of logo images to be displayed */
149
150     int i_repeat;         /* how often to repeat the images, image time in ms */
151     mtime_t i_next_pic;     /* when to bring up a new logo image */
152
153     unsigned int i_counter; /* index into the list of logo images */
154
155     int i_delay;            /* default delay (0 - 60000 ms) */
156     int i_alpha;            /* default alpha */
157
158     char *psz_filename;     /* --logo-file string ( is it really useful
159                              * to store it ? ) */
160
161     vlc_mutex_t lock;
162 } logo_list_t;
163
164 /*****************************************************************************
165  * LoadImage: loads the logo image into memory
166  *****************************************************************************/
167 static picture_t *LoadImage( vlc_object_t *p_this, char *psz_filename )
168 {
169     picture_t *p_pic;
170     image_handler_t *p_image;
171     video_format_t fmt_in;
172     video_format_t fmt_out;
173
174     memset( &fmt_in, 0, sizeof(video_format_t) );
175     memset( &fmt_out, 0, sizeof(video_format_t) );
176
177     fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
178     p_image = image_HandlerCreate( p_this );
179     p_pic = image_ReadUrl( p_image, psz_filename, &fmt_in, &fmt_out );
180     image_HandlerDelete( p_image );
181
182     return p_pic;
183 }
184
185 /*****************************************************************************
186  * LoadLogoList: loads the logo images into memory
187  *****************************************************************************
188  * Read the logo-file input switch, obtaining a list of images and associated
189  * durations and transparencies.  Store the image(s), and times.  An image
190  * without a stated time or transparency will use the logo-delay and
191  * logo-transparency values.
192  *****************************************************************************/
193 #define LoadLogoList( a, b ) __LoadLogoList( VLC_OBJECT( a ), b )
194 static void __LoadLogoList( vlc_object_t *p_this, logo_list_t *p_logo_list )
195 {
196     char *psz_list; /* the list: <logo>[,[<delay>[,[<alpha>]]]][;...] */
197     unsigned int i;
198     logo_t *p_logo;         /* the parsing's result */
199
200     p_logo_list->i_counter = 0;
201     p_logo_list->i_next_pic = 0;
202
203     psz_list = strdup( p_logo_list->psz_filename );
204
205     /* Count the number logos == number of ';' + 1 */
206     p_logo_list->i_count = 1;
207     for( i = 0; i < strlen( psz_list ); i++ )
208     {
209         if( psz_list[i] == ';' ) p_logo_list->i_count++;
210     }
211
212     p_logo_list->p_logo = p_logo =
213         (logo_t *)malloc( p_logo_list->i_count * sizeof(logo_t) );
214
215     /* Fill the data */
216     for( i = 0; i < p_logo_list->i_count; i++ )
217     {
218         char *p_c;
219         char *p_c2;
220         p_c = strchr( psz_list, ';' );
221         p_c2 = strchr( psz_list, ',' );
222
223         p_logo[i].i_alpha = -1; /* use default settings */
224         p_logo[i].i_delay = -1; /* use default settings */
225
226         if( p_c2 && ( p_c2 < p_c || !p_c ) )
227         {
228             /* <logo>,<delay>[,<alpha>] type */
229             if( p_c2[1] != ',' && p_c2[1] != ';' && p_c2[1] != '\0' )
230                 p_logo[i].i_delay = atoi( p_c2+1 );
231             *p_c2 = '\0';
232             if( ( p_c2 = strchr( p_c2+1, ',' ) )
233                 && ( p_c2 < p_c || !p_c ) && p_c2[1] != ';' && p_c2[1] != '\0' )
234                 p_logo[i].i_alpha = atoi( p_c2 + 1 );
235         }
236         else
237         {
238             /* <logo> type */
239             if( p_c ) *p_c = '\0';
240         }
241
242         p_logo[i].psz_file = strdup( psz_list );
243         p_logo[i].p_pic = LoadImage( p_this, p_logo[i].psz_file );
244
245         if( !p_logo[i].p_pic )
246         {
247             msg_Warn( p_this, "error while loading logo %s, will be skipped",
248                       p_logo[i].psz_file );
249         }
250
251         if( p_c ) psz_list = p_c + 1;
252     }
253
254     for( i = 0; i < p_logo_list->i_count; i++ )
255     {
256        msg_Dbg( p_this, "logo file name %s, delay %d, alpha %d",
257                 p_logo[i].psz_file, p_logo[i].i_delay, p_logo[i].i_alpha );
258     }
259
260     /* initialize so that on the first update it will wrap back to 0 */
261     p_logo_list->i_counter = p_logo_list->i_count;
262 }
263
264 /*****************************************************************************
265  * FreeLogoList
266  *****************************************************************************/
267 static void FreeLogoList( logo_list_t *p_logo_list )
268 {
269     unsigned int i;
270     FREENULL( p_logo_list->psz_filename );
271     for( i = 0; i < p_logo_list->i_count; i++ )
272     {
273         logo_t *p_logo = &p_logo_list->p_logo[i];
274         FREENULL( p_logo->psz_file );
275         if( p_logo->p_pic )
276         {
277             p_logo->p_pic->pf_release( p_logo->p_pic );
278             p_logo->p_pic = NULL;
279         }
280     }
281 }
282
283 /*****************************************************************************
284  * vout_sys_t: logo video output method descriptor
285  *****************************************************************************
286  * This structure is part of the video output thread descriptor.
287  * It describes the Invert specific properties of an output thread.
288  *****************************************************************************/
289 struct vout_sys_t
290 {
291     logo_list_t *p_logo_list;
292
293     vout_thread_t *p_vout;
294
295     filter_t *p_blend;
296
297     int i_width, i_height;
298     int pos, posx, posy;
299 };
300
301 /*****************************************************************************
302  * Create: allocates logo video thread output method
303  *****************************************************************************/
304 static int Create( vlc_object_t *p_this )
305 {
306     vout_thread_t *p_vout = (vout_thread_t *)p_this;
307     vout_sys_t *p_sys;
308     logo_list_t *p_logo_list;
309
310     /* Allocate structure */
311     p_sys = p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
312     if( p_sys == NULL )
313     {
314         msg_Err( p_vout, "out of memory" );
315         return VLC_ENOMEM;
316     }
317     p_logo_list = p_sys->p_logo_list = malloc( sizeof( logo_list_t ) );
318     if( p_logo_list == NULL )
319     {
320         msg_Err( p_vout, "out of memory" );
321         free( p_sys );
322         return VLC_ENOMEM;
323     }
324
325     p_vout->pf_init = Init;
326     p_vout->pf_end = End;
327     p_vout->pf_manage = NULL;
328     p_vout->pf_render = Render;
329     p_vout->pf_display = NULL;
330     p_vout->pf_control = Control;
331
332     config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options,
333                        p_vout->p_cfg );
334
335     p_logo_list->psz_filename = var_CreateGetStringCommand( p_vout,
336                                                             "logo-file" );
337     if( !p_logo_list->psz_filename || !*p_logo_list->psz_filename )
338     {
339         msg_Err( p_vout, "logo file not specified" );
340         return 0;
341     }
342
343     p_sys->pos = var_CreateGetIntegerCommand( p_vout, "logo-position" );
344     p_sys->posx = var_CreateGetIntegerCommand( p_vout, "logo-x" );
345     p_sys->posy = var_CreateGetIntegerCommand( p_vout, "logo-y" );
346     p_logo_list->i_delay = __MAX( __MIN(
347         var_CreateGetIntegerCommand( p_vout, "logo-delay" ) , 60000 ), 0 );
348     p_logo_list->i_repeat = var_CreateGetIntegerCommand( p_vout, "logo-repeat");
349     p_logo_list->i_alpha = __MAX( __MIN(
350         var_CreateGetIntegerCommand( p_vout, "logo-transparency" ), 255 ), 0 );
351
352     LoadLogoList( p_vout, p_logo_list );
353
354     return VLC_SUCCESS;
355 }
356
357 /*****************************************************************************
358  * Init: initialize logo video thread output method
359  *****************************************************************************/
360 static int Init( vout_thread_t *p_vout )
361 {
362     vout_sys_t *p_sys = p_vout->p_sys;
363     picture_t *p_pic;
364     int i_index;
365     video_format_t fmt;
366     logo_list_t *p_logo_list = p_sys->p_logo_list;
367
368     I_OUTPUTPICTURES = 0;
369     memset( &fmt, 0, sizeof(video_format_t) );
370
371     /* adjust index to the next logo */
372     p_logo_list->i_counter =
373                         ( p_logo_list->i_counter + 1 )%p_logo_list->i_count;
374
375     p_pic = p_logo_list->p_logo[p_logo_list->i_counter].p_pic;
376     /* Initialize the output structure */
377     p_vout->output.i_chroma = p_vout->render.i_chroma;
378     p_vout->output.i_width  = p_vout->render.i_width;
379     p_vout->output.i_height = p_vout->render.i_height;
380     p_vout->output.i_aspect = p_vout->render.i_aspect;
381     p_vout->fmt_out = p_vout->fmt_in;
382     fmt = p_vout->fmt_out;
383
384     /* Load the video blending filter */
385     p_sys->p_blend = vlc_object_create( p_vout, sizeof(filter_t) );
386     vlc_object_attach( p_sys->p_blend, p_vout );
387     p_sys->p_blend->fmt_out.video.i_x_offset =
388         p_sys->p_blend->fmt_out.video.i_y_offset = 0;
389     p_sys->p_blend->fmt_in.video.i_x_offset =
390         p_sys->p_blend->fmt_in.video.i_y_offset = 0;
391     p_sys->p_blend->fmt_out.video.i_aspect = p_vout->render.i_aspect;
392     p_sys->p_blend->fmt_out.video.i_chroma = p_vout->output.i_chroma;
393     p_sys->p_blend->fmt_in.video.i_chroma = VLC_FOURCC('Y','U','V','A');
394     p_sys->p_blend->fmt_in.video.i_aspect = VOUT_ASPECT_FACTOR;
395     p_sys->i_width =
396         p_sys->p_blend->fmt_in.video.i_width =
397             p_sys->p_blend->fmt_in.video.i_visible_width =
398                 p_pic ? p_pic->p[Y_PLANE].i_visible_pitch : 0;
399     p_sys->i_height =
400         p_sys->p_blend->fmt_in.video.i_height =
401             p_sys->p_blend->fmt_in.video.i_visible_height =
402                 p_pic ? p_pic->p[Y_PLANE].i_visible_lines : 0;
403     p_sys->p_blend->fmt_out.video.i_width =
404         p_sys->p_blend->fmt_out.video.i_visible_width =
405            p_vout->output.i_width;
406     p_sys->p_blend->fmt_out.video.i_height =
407         p_sys->p_blend->fmt_out.video.i_visible_height =
408             p_vout->output.i_height;
409
410     p_sys->p_blend->p_module =
411         module_Need( p_sys->p_blend, "video blending", 0, 0 );
412     if( !p_sys->p_blend->p_module )
413     {
414         msg_Err( p_vout, "can't open blending filter, aborting" );
415         vlc_object_detach( p_sys->p_blend );
416         vlc_object_destroy( p_sys->p_blend );
417         return VLC_EGENERIC;
418     }
419
420     if( p_sys->posx < 0 || p_sys->posy < 0 )
421     {
422         p_sys->posx = 0; p_sys->posy = 0;
423
424         if( p_sys->pos & SUBPICTURE_ALIGN_BOTTOM )
425         {
426             p_sys->posy = p_vout->render.i_height - p_sys->i_height;
427         }
428         else if ( !(p_sys->pos & SUBPICTURE_ALIGN_TOP) )
429         {
430             p_sys->posy = p_vout->render.i_height / 2 - p_sys->i_height / 2;
431         }
432
433         if( p_sys->pos & SUBPICTURE_ALIGN_RIGHT )
434         {
435             p_sys->posx = p_vout->render.i_width - p_sys->i_width;
436         }
437         else if ( !(p_sys->pos & SUBPICTURE_ALIGN_LEFT) )
438         {
439             p_sys->posx = p_vout->render.i_width / 2 - p_sys->i_width / 2;
440         }
441     }
442     else
443     {
444         p_sys->pos = 0;
445     }
446
447     /* Try to open the real video output */
448     msg_Dbg( p_vout, "spawning the real video output" );
449
450     p_sys->p_vout = vout_Create( p_vout, &fmt );
451
452     /* Everything failed */
453     if( p_sys->p_vout == NULL )
454     {
455         msg_Err( p_vout, "can't open vout, aborting" );
456         return VLC_EGENERIC;
457     }
458
459     var_AddCallback( p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
460     var_AddCallback( p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
461
462     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
463     ADD_CALLBACKS( p_sys->p_vout, SendEvents );
464     ADD_PARENT_CALLBACKS( SendEventsToChild );
465
466     return VLC_SUCCESS;
467 }
468
469 /*****************************************************************************
470  * End: terminate logo video thread output method
471  *****************************************************************************/
472 static void End( vout_thread_t *p_vout )
473 {
474     vout_sys_t *p_sys = p_vout->p_sys;
475     int i_index;
476
477     /* Free the fake output buffers we allocated */
478     for( i_index = I_OUTPUTPICTURES ; i_index ; )
479     {
480         i_index--;
481         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
482     }
483
484     var_DelCallback( p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
485     var_DelCallback( p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
486
487     if( p_sys->p_vout )
488     {
489         DEL_CALLBACKS( p_sys->p_vout, SendEvents );
490         vlc_object_detach( p_sys->p_vout );
491         vout_Destroy( p_sys->p_vout );
492     }
493
494     if( p_sys->p_blend->p_module )
495         module_Unneed( p_sys->p_blend, p_sys->p_blend->p_module );
496     vlc_object_detach( p_sys->p_blend );
497     vlc_object_destroy( p_sys->p_blend );
498 }
499
500 /*****************************************************************************
501  * Destroy: destroy logo video thread output method
502  *****************************************************************************/
503 static void Destroy( vlc_object_t *p_this )
504 {
505     vout_thread_t *p_vout = (vout_thread_t *)p_this;
506     vout_sys_t *p_sys = p_vout->p_sys;
507
508     DEL_PARENT_CALLBACKS( SendEventsToChild );
509
510     FreeLogoList( p_sys->p_logo_list );
511     free( p_sys->p_logo_list );
512
513     free( p_sys );
514 }
515
516 /*****************************************************************************
517  * Render: render the logo onto the video
518  *****************************************************************************/
519 static void Render( vout_thread_t *p_vout, picture_t *p_inpic )
520 {
521     vout_sys_t *p_sys = p_vout->p_sys;
522     picture_t *p_outpic;
523     picture_t *p_pic;
524     logo_list_t *p_logo_list;
525     logo_t * p_logo;
526
527     p_logo_list = p_sys->p_logo_list;
528
529     if( p_logo_list->i_next_pic < p_inpic->date )
530     {
531         /* It's time to use a new logo */
532         p_logo_list->i_counter =
533                         ( p_logo_list->i_counter + 1 )%p_logo_list->i_count;
534         p_logo = &p_logo_list->p_logo[p_sys->p_logo_list->i_counter];
535         p_pic = p_logo->p_pic;
536         p_logo_list->i_next_pic = p_inpic->date + ( p_logo->i_delay != -1 ?
537                               p_logo->i_delay : p_logo_list->i_delay ) * 1000;
538         if( p_pic )
539         {
540
541             p_sys->i_width =
542                 p_sys->p_blend->fmt_in.video.i_width =
543                     p_sys->p_blend->fmt_in.video.i_visible_width =
544                         p_pic->p[Y_PLANE].i_visible_pitch;
545             p_sys->i_height =
546                 p_sys->p_blend->fmt_in.video.i_height =
547                     p_sys->p_blend->fmt_in.video.i_visible_height =
548                         p_pic->p[Y_PLANE].i_visible_lines;
549
550             if( p_sys->pos )
551             {
552                 if( p_sys->pos & SUBPICTURE_ALIGN_BOTTOM )
553                 {
554                     p_sys->posy = p_vout->render.i_height - p_sys->i_height;
555                 }
556                 else if ( !(p_sys->pos & SUBPICTURE_ALIGN_TOP) )
557                 {
558                     p_sys->posy = p_vout->render.i_height/2 - p_sys->i_height/2;
559                 }
560                 if( p_sys->pos & SUBPICTURE_ALIGN_RIGHT )
561                 {
562                     p_sys->posx = p_vout->render.i_width - p_sys->i_width;
563                 }
564                 else if ( !(p_sys->pos & SUBPICTURE_ALIGN_LEFT) )
565                 {
566                     p_sys->posx = p_vout->render.i_width/2 - p_sys->i_width/2;
567                 }
568             }
569         }
570
571     }
572     else
573     {
574         p_logo = &p_logo_list->p_logo[p_sys->p_logo_list->i_counter];
575         p_pic = p_logo->p_pic;
576     }
577
578     /* This is a new frame. Get a structure from the video_output. */
579     while( !(p_outpic = vout_CreatePicture( p_sys->p_vout, 0, 0, 0 )) )
580     {
581         if( p_vout->b_die || p_vout->b_error ) return;
582         msleep( VOUT_OUTMEM_SLEEP );
583     }
584
585     vout_CopyPicture( p_vout, p_outpic, p_inpic );
586     vout_DatePicture( p_sys->p_vout, p_outpic, p_inpic->date );
587
588     if( p_pic )
589     p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_outpic, p_outpic,
590                                     p_pic, p_sys->posx, p_sys->posy,
591                                     p_logo->i_alpha != -1 ? p_logo->i_alpha
592                                     : p_logo_list->i_alpha );
593
594     vout_DisplayPicture( p_sys->p_vout, p_outpic );
595 }
596
597 /*****************************************************************************
598  * SendEvents: forward mouse and keyboard events to the parent p_vout
599  *****************************************************************************/
600 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
601                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
602 {
603     var_Set( (vlc_object_t *)p_data, psz_var, newval );
604     return VLC_SUCCESS;
605 }
606
607 /*****************************************************************************
608  * MouseEvent: callback for mouse events
609  *****************************************************************************/
610 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
611                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
612 {
613     vout_thread_t *p_vout = (vout_thread_t*)p_data;
614     vout_sys_t *p_sys = p_vout->p_sys;
615     vlc_value_t valb;
616     int i_delta;
617
618     var_Get( p_vout->p_sys->p_vout, "mouse-button-down", &valb );
619
620     i_delta = newval.i_int - oldval.i_int;
621
622     if( (valb.i_int & 0x1) == 0 )
623     {
624         return VLC_SUCCESS;
625     }
626
627     if( psz_var[6] == 'x' )
628     {
629         vlc_value_t valy;
630         var_Get( p_vout->p_sys->p_vout, "mouse-y", &valy );
631         if( newval.i_int >= (int)p_sys->posx &&
632             valy.i_int >= (int)p_sys->posy &&
633             newval.i_int <= (int)(p_sys->posx + p_sys->i_width) &&
634             valy.i_int <= (int)(p_sys->posy + p_sys->i_height) )
635         {
636             p_sys->posx = __MIN( __MAX( p_sys->posx + i_delta, 0 ),
637                           p_vout->output.i_width - p_sys->i_width );
638         }
639     }
640     else if( psz_var[6] == 'y' )
641     {
642         vlc_value_t valx;
643         var_Get( p_vout->p_sys->p_vout, "mouse-x", &valx );
644         if( valx.i_int >= (int)p_sys->posx &&
645             newval.i_int >= (int)p_sys->posy &&
646             valx.i_int <= (int)(p_sys->posx + p_sys->i_width) &&
647             newval.i_int <= (int)(p_sys->posy + p_sys->i_height) )
648         {
649             p_sys->posy = __MIN( __MAX( p_sys->posy + i_delta, 0 ),
650                           p_vout->output.i_height - p_sys->i_height );
651         }
652     }
653
654     return VLC_SUCCESS;
655 }
656
657 /*****************************************************************************
658  * Control: control facility for the vout (forwards to child vout)
659  *****************************************************************************/
660 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
661 {
662     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
663 }
664
665 /*****************************************************************************
666  * SendEventsToChild: forward events to the child/children vout
667  *****************************************************************************/
668 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
669                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
670 {
671     vout_thread_t *p_vout = (vout_thread_t *)p_this;
672     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
673     return VLC_SUCCESS;
674 }
675
676 /*****************************************************************************
677  * filter_sys_t: logo filter descriptor
678  *****************************************************************************/
679 struct filter_sys_t
680 {
681     logo_list_t *p_logo_list;
682
683     int pos, posx, posy;
684
685     vlc_bool_t b_absolute;
686     mtime_t i_last_date;
687
688     /* On the fly control variable */
689     vlc_bool_t b_need_update;
690 };
691
692 static subpicture_t *Filter( filter_t *, mtime_t );
693
694 /*****************************************************************************
695  * CreateFilter: allocates logo video filter
696  *****************************************************************************/
697 static int CreateFilter( vlc_object_t *p_this )
698 {
699     filter_t *p_filter = (filter_t *)p_this;
700     filter_sys_t *p_sys;
701     logo_list_t *p_logo_list;
702
703     /* Allocate structure */
704     p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
705     if( p_sys == NULL )
706     {
707         msg_Err( p_filter, "out of memory" );
708         return VLC_ENOMEM;
709     }
710     p_logo_list = p_sys->p_logo_list = malloc( sizeof( logo_list_t ) );
711     if( p_logo_list == NULL )
712     {
713         msg_Err( p_filter, "out of memory" );
714         free( p_sys );
715         return VLC_ENOMEM;
716     }
717
718     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
719                        p_filter->p_cfg );
720
721     /* Hook used for callback variables */
722     p_logo_list->psz_filename =
723         var_CreateGetStringCommand( p_filter, "logo-file" );
724     if( !p_logo_list->psz_filename || !*p_logo_list->psz_filename )
725     {
726         msg_Err( p_this, "logo file not specified" );
727         free( p_sys );
728         free( p_logo_list );
729         return VLC_EGENERIC;
730     }
731
732     p_sys->posx = var_CreateGetIntegerCommand( p_filter, "logo-x" );
733     p_sys->posy = var_CreateGetIntegerCommand( p_filter, "logo-y" );
734     p_sys->pos = var_CreateGetIntegerCommand( p_filter, "logo-position" );
735     p_logo_list->i_alpha = __MAX( __MIN( var_CreateGetIntegerCommand(
736                            p_filter, "logo-transparency"), 255 ), 0 );
737     p_logo_list->i_delay =
738         var_CreateGetIntegerCommand( p_filter, "logo-delay" );
739     p_logo_list->i_repeat =
740         var_CreateGetIntegerCommand( p_filter, "logo-repeat" );
741
742     var_AddCallback( p_filter, "logo-file", LogoCallback, p_sys );
743     var_AddCallback( p_filter, "logo-x", LogoCallback, p_sys );
744     var_AddCallback( p_filter, "logo-y", LogoCallback, p_sys );
745     var_AddCallback( p_filter, "logo-position", LogoCallback, p_sys );
746     var_AddCallback( p_filter, "logo-transparency", LogoCallback, p_sys );
747     var_AddCallback( p_filter, "logo-repeat", LogoCallback, p_sys );
748
749     vlc_mutex_init( p_filter, &p_logo_list->lock );
750     vlc_mutex_lock( &p_logo_list->lock );
751
752     LoadLogoList( p_this, p_logo_list );
753
754     vlc_mutex_unlock( &p_logo_list->lock );
755
756     /* Misc init */
757     p_filter->pf_sub_filter = Filter;
758     p_sys->b_need_update = VLC_TRUE;
759
760     p_sys->i_last_date = 0;
761
762     return VLC_SUCCESS;
763 }
764
765 /*****************************************************************************
766  * DestroyFilter: destroy logo video filter
767  *****************************************************************************/
768 static void DestroyFilter( vlc_object_t *p_this )
769 {
770     filter_t *p_filter = (filter_t *)p_this;
771     filter_sys_t *p_sys = p_filter->p_sys;
772
773     vlc_mutex_destroy( &p_sys->p_logo_list->lock );
774     FreeLogoList( p_sys->p_logo_list );
775     free( p_sys->p_logo_list );
776     free( p_sys );
777
778     /* Delete the logo variables from INPUT */
779     var_Destroy( p_filter->p_libvlc, "logo-file" );
780     var_Destroy( p_filter->p_libvlc, "logo-x" );
781     var_Destroy( p_filter->p_libvlc, "logo-y" );
782     var_Destroy( p_filter->p_libvlc, "logo-delay" );
783     var_Destroy( p_filter->p_libvlc, "logo-repeat" );
784     var_Destroy( p_filter->p_libvlc, "logo-position" );
785     var_Destroy( p_filter->p_libvlc, "logo-transparency" );
786 }
787
788 /*****************************************************************************
789  * Filter: the whole thing
790  *****************************************************************************
791  * This function outputs subpictures at regular time intervals.
792  *****************************************************************************/
793 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
794 {
795     filter_sys_t *p_sys = p_filter->p_sys;
796     logo_list_t *p_logo_list = p_sys->p_logo_list;
797     subpicture_t *p_spu;
798     subpicture_region_t *p_region;
799     video_format_t fmt;
800     picture_t *p_pic;
801     logo_t *p_logo;
802
803     vlc_mutex_lock( &p_logo_list->lock );
804     /* Basic test:  b_need_update occurs on a dynamic change,
805                     & i_next_pic is the general timer, when to
806                     look at updating the logo image */
807
808     if( ( ( !p_sys->b_need_update ) && ( p_logo_list->i_next_pic > date ) )
809         || !p_logo_list->i_repeat )
810     {
811         vlc_mutex_unlock( &p_logo_list->lock );
812         return 0;
813     }
814     /* prior code tested on && p_sys->i_last_date +5000000 > date ) return 0; */
815
816     /* adjust index to the next logo */
817     p_logo_list->i_counter =
818                         ( p_logo_list->i_counter + 1 )%p_logo_list->i_count;
819
820     p_logo = &p_logo_list->p_logo[p_logo_list->i_counter];
821     p_pic = p_logo->p_pic;
822
823     /* Allocate the subpicture internal data. */
824     p_spu = p_filter->pf_sub_buffer_new( p_filter );
825     if( !p_spu )
826     {
827         vlc_mutex_unlock( &p_logo_list->lock );
828         return NULL;
829     }
830
831     p_spu->b_absolute = p_sys->b_absolute;
832     p_spu->i_start = p_sys->i_last_date = date;
833     p_spu->i_stop = 0;
834     p_spu->b_ephemer = VLC_TRUE;
835
836     p_sys->b_need_update = VLC_FALSE;
837     p_logo_list->i_next_pic = date +
838     ( p_logo->i_delay != -1 ? p_logo->i_delay : p_logo_list->i_delay ) * 1000;
839
840     if( p_logo_list->i_repeat != -1
841         && p_logo_list->i_counter == 0 )
842     {
843         p_logo_list->i_repeat--;
844         if( p_logo_list->i_repeat == 0 )
845         {
846             vlc_mutex_unlock( &p_logo_list->lock );
847             return p_spu;
848         }
849     }
850
851     if( !p_pic || !p_logo->i_alpha
852         || ( p_logo->i_alpha == -1 && !p_logo_list->i_alpha ) )
853     {
854         /* Send an empty subpicture to clear the display */
855         vlc_mutex_unlock( &p_logo_list->lock );
856         return p_spu;
857     }
858
859     /* Create new SPU region */
860     memset( &fmt, 0, sizeof(video_format_t) );
861     fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
862     fmt.i_aspect = VOUT_ASPECT_FACTOR;
863     fmt.i_sar_num = fmt.i_sar_den = 1;
864     fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
865     fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
866     fmt.i_x_offset = fmt.i_y_offset = 0;
867     p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
868     if( !p_region )
869     {
870         msg_Err( p_filter, "cannot allocate SPU region" );
871         p_filter->pf_sub_buffer_del( p_filter, p_spu );
872         vlc_mutex_unlock( &p_logo_list->lock );
873         return NULL;
874     }
875
876     vout_CopyPicture( p_filter, &p_region->picture, p_pic );
877     vlc_mutex_unlock( &p_logo_list->lock );
878
879     /*  where to locate the logo: */
880     if( p_sys->posx < 0 || p_sys->posy < 0 )
881     {   /* set to one of the 9 relative locations */
882         p_region->i_align = p_sys->pos;
883         p_spu->i_x = 0;
884         p_spu->i_y = 0;
885         p_spu->b_absolute = VLC_FALSE;
886     }
887     else
888     {   /*  set to an absolute xy, referenced to upper left corner */
889         p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
890         p_spu->i_x = p_sys->posx;
891         p_spu->i_y = p_sys->posy;
892         p_spu->b_absolute = VLC_TRUE;
893     }
894
895     p_spu->p_region = p_region;
896
897     p_spu->i_alpha = ( p_logo->i_alpha != -1 ?
898                        p_logo->i_alpha : p_logo_list->i_alpha );
899
900     return p_spu;
901 }
902
903 /*****************************************************************************
904  * Callback to update params on the fly
905  *****************************************************************************/
906 static int LogoCallback( vlc_object_t *p_this, char const *psz_var,
907                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
908 {
909     filter_sys_t *p_sys = (filter_sys_t *)p_data;
910     logo_list_t *p_logo_list = p_sys->p_logo_list;
911
912     if( !strncmp( psz_var, "logo-file", 6 ) )
913     {
914         vlc_mutex_lock( &p_logo_list->lock );
915         FreeLogoList( p_logo_list );
916         p_logo_list->psz_filename = strdup( newval.psz_string );
917         LoadLogoList( p_this, p_logo_list );
918         vlc_mutex_unlock( &p_logo_list->lock );
919         p_sys->b_need_update = VLC_TRUE;
920     }
921     else if ( !strncmp( psz_var, "logo-x", 6 ) )
922     {
923         p_sys->posx = newval.i_int;
924     }
925     else if ( !strncmp( psz_var, "logo-y", 6 ) )
926     {
927         p_sys->posy = newval.i_int;
928     }
929     else if ( !strncmp( psz_var, "logo-position", 12 ) )
930     {
931         p_sys->pos = newval.i_int;
932     }
933     else if ( !strncmp( psz_var, "logo-transparency", 9 ) )
934     {
935         vlc_mutex_lock( &p_logo_list->lock );
936         p_logo_list->i_alpha = __MAX( __MIN( newval.i_int, 255 ), 0 );
937         vlc_mutex_unlock( &p_logo_list->lock );
938     }
939     else if ( !strncmp( psz_var, "logo-repeat", 11 ) )
940     {
941         vlc_mutex_lock( &p_logo_list->lock );
942         p_logo_list->i_repeat = newval.i_int;
943         vlc_mutex_unlock( &p_logo_list->lock );
944     }
945     p_sys->b_need_update = VLC_TRUE;
946     return VLC_SUCCESS;
947 }