]> git.sesse.net Git - vlc/blob - modules/video_filter/logo.c
Improvements to preferences
[vlc] / modules / video_filter / logo.c
1 /*****************************************************************************
2  * logo.c : logo video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2004 VideoLAN
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30
31 #include <png.h>
32
33 #include <vlc/vlc.h>
34 #include <vlc/vout.h>
35
36 #include "vlc_filter.h"
37 #include "filter_common.h"
38
39 /*****************************************************************************
40  * Local prototypes
41  *****************************************************************************/
42 static int  Create    ( vlc_object_t * );
43 static void Destroy   ( vlc_object_t * );
44
45 static int  Init      ( vout_thread_t * );
46 static void End       ( vout_thread_t * );
47 static void Render    ( vout_thread_t *, picture_t * );
48
49 static int  SendEvents( vlc_object_t *, char const *,
50                         vlc_value_t, vlc_value_t, void * );
51 static int MouseEvent ( vlc_object_t *, char const *,
52                         vlc_value_t , vlc_value_t , void * );
53 static int Control    ( vout_thread_t *, int, va_list );
54
55 static int  CreateFilter ( vlc_object_t * );
56 static void DestroyFilter( vlc_object_t * );
57
58 static int LogoCallback( vlc_object_t *, char const *,
59                          vlc_value_t, vlc_value_t, void * );
60
61 /*****************************************************************************
62  * Module descriptor
63  *****************************************************************************/
64 #define FILE_TEXT N_("Logo filename")
65 #define FILE_LONGTEXT N_("Full path of the PNG file to use.")
66 #define POSX_TEXT N_("X coordinate of the logo")
67 #define POSX_LONGTEXT N_("You can move the logo by left-clicking on it." )
68 #define POSY_TEXT N_("Y coordinate of the logo")
69 #define POSY_LONGTEXT N_("You can move the logo by left-clicking on it." )
70 #define TRANS_TEXT N_("Transparency of the logo")
71 #define TRANS_LONGTEXT N_("You can set the logo transparency value here " \
72   "(from 0 for full transparency to 255 for full opacity)." )
73 #define POS_TEXT N_("Logo position")
74 #define POS_LONGTEXT N_( \
75   "You can enforce the logo position on the video " \
76   "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
77   "also use combinations of these values).")
78
79 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
80 static char *ppsz_pos_descriptions[] =
81 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
82   N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
83
84 vlc_module_begin();
85     set_description( _("Logo video filter") );
86     set_capability( "video filter", 0 );
87     set_category( CAT_VIDEO );
88     set_subcategory( SUBCAT_VIDEO_VFILTER );
89     add_shortcut( "logo" );
90     set_callbacks( Create, Destroy );
91
92     add_file( "logo-file", NULL, NULL, FILE_TEXT, FILE_LONGTEXT, VLC_FALSE );
93     add_integer( "logo-x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_FALSE );
94     add_integer( "logo-y", -1, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_FALSE );
95     add_integer_with_range( "logo-transparency", 255, 0, 255, NULL,
96         TRANS_TEXT, TRANS_LONGTEXT, VLC_FALSE );
97     add_integer( "logo-position", 6, NULL, POS_TEXT, POS_LONGTEXT, VLC_TRUE );
98         change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
99
100     /* subpicture filter submodule */
101     add_submodule();
102     set_capability( "sub filter", 0 );
103     set_callbacks( CreateFilter, DestroyFilter );
104     set_description( _("Logo sub filter") );
105     add_shortcut( "logo" );
106 vlc_module_end();
107
108 /*****************************************************************************
109  * LoadPNG: loads the PNG logo into memory
110  *****************************************************************************/
111 static picture_t *LoadPNG( vlc_object_t *p_this, char *psz_filename,
112                            int i_trans )
113 {
114     picture_t *p_pic;
115     FILE *file;
116     int i, j;
117     vlc_bool_t b_alpha = VLC_TRUE;
118
119     png_uint_32 i_width, i_height;
120     int i_color_type, i_interlace_type, i_compression_type, i_filter_type;
121     int i_bit_depth;
122     png_bytep *p_row_pointers;
123     png_structp p_png;
124     png_infop p_info, p_end_info;
125
126     if( !(file = fopen( psz_filename , "rb" )) )
127     {
128         msg_Err( p_this, "logo file (%s) not found", psz_filename );
129         return 0;
130     }
131
132     p_png = png_create_read_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 );
133     p_info = png_create_info_struct( p_png );
134     p_end_info = png_create_info_struct( p_png );
135     png_init_io( p_png, file );
136     png_read_info( p_png, p_info );
137     png_get_IHDR( p_png, p_info, &i_width, &i_height,
138                   &i_bit_depth, &i_color_type, &i_interlace_type,
139                   &i_compression_type, &i_filter_type);
140
141     if( i_color_type == PNG_COLOR_TYPE_PALETTE )
142         png_set_palette_to_rgb( p_png );
143
144     if( i_color_type == PNG_COLOR_TYPE_GRAY ||
145         i_color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
146           png_set_gray_to_rgb( p_png );
147
148     if( png_get_valid( p_png, p_info, PNG_INFO_tRNS ) )
149     {
150         png_set_tRNS_to_alpha( p_png );
151     }
152     else if( !(i_color_type & PNG_COLOR_MASK_ALPHA) )
153     {
154         b_alpha = VLC_FALSE;
155     }
156
157     p_row_pointers = malloc( sizeof(png_bytep) * i_height );
158     for( i = 0; i < (int)i_height; i++ )
159         p_row_pointers[i] = malloc( 4 * ( i_bit_depth + 7 ) / 8 * i_width );
160
161     png_read_image( p_png, p_row_pointers );
162     png_read_end( p_png, p_end_info );
163
164     fclose( file );
165     png_destroy_read_struct( &p_png, &p_info, &p_end_info );
166
167     /* Convert to YUVA */
168     p_pic = malloc( sizeof(picture_t) );
169     if( vout_AllocatePicture( p_this, p_pic, VLC_FOURCC('Y','U','V','A'),
170                               i_width, i_height, VOUT_ASPECT_FACTOR ) !=
171         VLC_SUCCESS )
172     {
173         for( i = 0; i < (int)i_height; i++ ) free( p_row_pointers[i] );
174         free( p_row_pointers );
175         return 0;
176     }
177
178     for( j = 0; j < (int)i_height ; j++ )
179     {
180         uint8_t *p = (uint8_t *)p_row_pointers[j];
181
182         for( i = 0; i < (int)i_width ; i++ )
183         {
184             int i_offset = i + j * p_pic->p[Y_PLANE].i_pitch;
185
186             p_pic->p[Y_PLANE].p_pixels[i_offset] =
187                 (p[0] * 257L + p[1] * 504 + p[2] * 98)/1000 + 16;
188             p_pic->p[U_PLANE].p_pixels[i_offset] =
189                 (p[2] * 439L - p[0] * 148 - p[1] * 291)/1000 + 128;
190             p_pic->p[V_PLANE].p_pixels[i_offset] =
191                 (p[0] * 439L - p[1] * 368 - p[2] * 71)/1000 + 128;
192             p_pic->p[A_PLANE].p_pixels[i_offset] =
193                 b_alpha ? (p[3] * i_trans) / 255 : i_trans;
194
195             p += (b_alpha ? 4 : 3);
196         }
197     }
198
199     for( i = 0; i < (int)i_height; i++ ) free( p_row_pointers[i] );
200     free( p_row_pointers );
201     return p_pic;
202 }
203
204 /*****************************************************************************
205  * vout_sys_t: logo video output method descriptor
206  *****************************************************************************
207  * This structure is part of the video output thread descriptor.
208  * It describes the Invert specific properties of an output thread.
209  *****************************************************************************/
210 struct vout_sys_t
211 {
212     vout_thread_t *p_vout;
213
214     filter_t *p_blend;
215     picture_t *p_pic;
216
217     int i_width, i_height;
218     int pos, posx, posy;
219     char *psz_filename;
220     int i_trans;
221 };
222
223 /*****************************************************************************
224  * Create: allocates logo video thread output method
225  *****************************************************************************/
226 static int Create( vlc_object_t *p_this )
227 {
228     vout_thread_t *p_vout = (vout_thread_t *)p_this;
229     vout_sys_t *p_sys;
230     vlc_value_t val;
231
232     /* Allocate structure */
233     p_sys = p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
234     if( p_sys == NULL )
235     {
236         msg_Err( p_vout, "out of memory" );
237         return VLC_ENOMEM;
238     }
239
240     p_vout->pf_init = Init;
241     p_vout->pf_end = End;
242     p_vout->pf_manage = NULL;
243     p_vout->pf_render = Render;
244     p_vout->pf_display = NULL;
245     p_vout->pf_control = Control;
246     
247     p_sys->psz_filename = var_CreateGetString( p_this , "logo-file" ); 
248     if( !p_sys->psz_filename || !*p_sys->psz_filename )
249     {
250         msg_Err( p_this, "logo file not specified" );
251         return 0;
252     }
253
254     var_Create( p_this, "logo-position", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
255     var_Get( p_this, "logo-position", &val );
256     p_sys->pos = val.i_int;
257     var_Create( p_this, "logo-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
258     var_Get( p_this, "logo-x", &val );
259     p_sys->posx = val.i_int;
260     var_Create( p_this, "logo-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
261     var_Get( p_this, "logo-y", &val );
262     p_sys->posy = val.i_int;
263     var_Create(p_this, "logo-transparency", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT);
264     var_Get( p_this, "logo-transparency", &val );
265     p_sys->i_trans = __MAX( __MIN( val.i_int, 255 ), 0 );
266
267     p_sys->p_pic = LoadPNG( p_this, p_sys->psz_filename, p_sys->i_trans );
268     if( !p_sys->p_pic )
269     {
270         free( p_sys );
271         return VLC_EGENERIC;
272     }
273
274     p_sys->i_width = p_sys->p_pic->p[Y_PLANE].i_visible_pitch;
275     p_sys->i_height = p_sys->p_pic->p[Y_PLANE].i_visible_lines;
276
277     return VLC_SUCCESS;
278 }
279
280 /*****************************************************************************
281  * Init: initialize logo video thread output method
282  *****************************************************************************/
283 static int Init( vout_thread_t *p_vout )
284 {
285     vout_sys_t *p_sys = p_vout->p_sys;
286     picture_t *p_pic;
287     int i_index;
288
289     I_OUTPUTPICTURES = 0;
290
291     /* Initialize the output structure */
292     p_vout->output.i_chroma = p_vout->render.i_chroma;
293     p_vout->output.i_width  = p_vout->render.i_width;
294     p_vout->output.i_height = p_vout->render.i_height;
295     p_vout->output.i_aspect = p_vout->render.i_aspect;
296
297     /* Load the video blending filter */
298     p_sys->p_blend = vlc_object_create( p_vout, sizeof(filter_t) );
299     vlc_object_attach( p_sys->p_blend, p_vout );
300     p_sys->p_blend->fmt_out.video.i_x_offset =
301         p_sys->p_blend->fmt_out.video.i_y_offset = 0;
302     p_sys->p_blend->fmt_in.video.i_x_offset =
303         p_sys->p_blend->fmt_in.video.i_y_offset = 0;
304     p_sys->p_blend->fmt_out.video.i_aspect = p_vout->render.i_aspect;
305     p_sys->p_blend->fmt_out.video.i_chroma = p_vout->output.i_chroma;
306     p_sys->p_blend->fmt_in.video.i_chroma = VLC_FOURCC('Y','U','V','A');
307     p_sys->p_blend->fmt_in.video.i_aspect = VOUT_ASPECT_FACTOR;
308     p_sys->p_blend->fmt_in.video.i_width =
309         p_sys->p_blend->fmt_in.video.i_visible_width =
310             p_sys->p_pic->p[Y_PLANE].i_visible_pitch;
311     p_sys->p_blend->fmt_in.video.i_height =
312         p_sys->p_blend->fmt_in.video.i_visible_height =
313             p_sys->p_pic->p[Y_PLANE].i_visible_lines;
314     p_sys->p_blend->fmt_out.video.i_width =
315         p_sys->p_blend->fmt_out.video.i_visible_width =
316            p_vout->output.i_width;
317     p_sys->p_blend->fmt_out.video.i_height =
318         p_sys->p_blend->fmt_out.video.i_visible_height =
319             p_vout->output.i_height;
320
321     p_sys->p_blend->p_module =
322         module_Need( p_sys->p_blend, "video blending", 0, 0 );
323     if( !p_sys->p_blend->p_module )
324     {
325         msg_Err( p_vout, "can't open blending filter, aborting" );
326         vlc_object_detach( p_sys->p_blend );
327         vlc_object_destroy( p_sys->p_blend );
328         return VLC_EGENERIC;
329     }
330
331     if( p_sys->posx < 0 || p_sys->posy < 0 )
332     {
333         p_sys->posx = 0; p_sys->posy = 0;
334
335         if( p_sys->pos & SUBPICTURE_ALIGN_BOTTOM )
336         {
337             p_sys->posy = p_vout->render.i_height - p_sys->i_height;
338         }
339         else if ( !(p_sys->pos & SUBPICTURE_ALIGN_TOP) )
340         {
341             p_sys->posy = p_vout->render.i_height / 2 - p_sys->i_height / 2;
342         }
343
344         if( p_sys->pos & SUBPICTURE_ALIGN_RIGHT )
345         {
346             p_sys->posx = p_vout->render.i_width - p_sys->i_width;
347         }
348         else if ( !(p_sys->pos & SUBPICTURE_ALIGN_LEFT) )
349         {
350             p_sys->posx = p_vout->render.i_width / 2 - p_sys->i_width / 2;
351         }
352     }
353
354     /* Try to open the real video output */
355     msg_Dbg( p_vout, "spawning the real video output" );
356
357     p_sys->p_vout =
358         vout_Create( p_vout, p_vout->render.i_width, p_vout->render.i_height,
359                      p_vout->render.i_chroma, p_vout->render.i_aspect );
360
361     /* Everything failed */
362     if( p_sys->p_vout == NULL )
363     {
364         msg_Err( p_vout, "can't open vout, aborting" );
365         return VLC_EGENERIC;
366     }
367
368     var_AddCallback( p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
369     var_AddCallback( p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
370
371     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
372     ADD_CALLBACKS( p_sys->p_vout, SendEvents );
373     ADD_PARENT_CALLBACKS( SendEventsToChild );
374
375     return VLC_SUCCESS;
376 }
377
378 /*****************************************************************************
379  * End: terminate logo video thread output method
380  *****************************************************************************/
381 static void End( vout_thread_t *p_vout )
382 {
383     vout_sys_t *p_sys = p_vout->p_sys;
384     int i_index;
385
386     /* Free the fake output buffers we allocated */
387     for( i_index = I_OUTPUTPICTURES ; i_index ; )
388     {
389         i_index--;
390         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
391     }
392
393     var_DelCallback( p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
394     var_DelCallback( p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
395
396     if( p_sys->p_vout )
397     {
398         DEL_CALLBACKS( p_sys->p_vout, SendEvents );
399         vlc_object_detach( p_sys->p_vout );
400         vout_Destroy( p_sys->p_vout );
401     }
402
403     if( p_sys->p_blend->p_module )
404         module_Unneed( p_sys->p_blend, p_sys->p_blend->p_module );
405     vlc_object_detach( p_sys->p_blend );
406     vlc_object_destroy( p_sys->p_blend );
407 }
408
409 /*****************************************************************************
410  * Destroy: destroy logo video thread output method
411  *****************************************************************************/
412 static void Destroy( vlc_object_t *p_this )
413 {
414     vout_thread_t *p_vout = (vout_thread_t *)p_this;
415     vout_sys_t *p_sys = p_vout->p_sys;
416
417     DEL_PARENT_CALLBACKS( SendEventsToChild );
418
419     if( p_sys->p_pic && p_sys->p_pic->p_data_orig )
420         free( p_sys->p_pic->p_data_orig );
421     if( p_sys->p_pic ) free( p_sys->p_pic );
422
423     free( p_sys );
424 }
425
426 /*****************************************************************************
427  * Render: render the logo onto the video
428  *****************************************************************************/
429 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
430 {
431     vout_sys_t *p_sys = p_vout->p_sys;
432     picture_t *p_outpic;
433
434     /* This is a new frame. Get a structure from the video_output. */
435     while( !(p_outpic = vout_CreatePicture( p_sys->p_vout, 0, 0, 0 )) )
436     {
437         if( p_vout->b_die || p_vout->b_error ) return;
438         msleep( VOUT_OUTMEM_SLEEP );
439     }
440
441     vout_CopyPicture( p_vout, p_outpic, p_pic );
442     vout_DatePicture( p_sys->p_vout, p_outpic, p_pic->date );
443
444     p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_outpic, p_outpic,
445                                     p_sys->p_pic, p_sys->posx, p_sys->posy,
446                                     255 );
447
448     vout_DisplayPicture( p_sys->p_vout, p_outpic );
449 }
450
451 /*****************************************************************************
452  * SendEvents: forward mouse and keyboard events to the parent p_vout
453  *****************************************************************************/
454 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
455                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
456 {
457     var_Set( (vlc_object_t *)p_data, psz_var, newval );
458     return VLC_SUCCESS;
459 }
460
461 /*****************************************************************************
462  * MouseEvent: callback for mouse events
463  *****************************************************************************/
464 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
465                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
466 {
467     vout_thread_t *p_vout = (vout_thread_t*)p_data;
468     vout_sys_t *p_sys = p_vout->p_sys;
469     vlc_value_t valb;
470     int i_delta;
471
472     var_Get( p_vout->p_sys->p_vout, "mouse-button-down", &valb );
473
474     i_delta = newval.i_int - oldval.i_int;
475
476     if( (valb.i_int & 0x1) == 0 )
477     {
478         return VLC_SUCCESS;
479     }
480
481     if( psz_var[6] == 'x' )
482     {
483         vlc_value_t valy;
484         var_Get( p_vout->p_sys->p_vout, "mouse-y", &valy );
485         if( newval.i_int >= (int)p_sys->posx &&
486             valy.i_int >= (int)p_sys->posy &&
487             newval.i_int <= (int)(p_sys->posx + p_sys->i_width) &&
488             valy.i_int <= (int)(p_sys->posy + p_sys->i_height) )
489         {
490             p_sys->posx = __MIN( __MAX( p_sys->posx + i_delta, 0 ),
491                           p_vout->output.i_width - p_sys->i_width );
492         }
493     }
494     else if( psz_var[6] == 'y' )
495     {
496         vlc_value_t valx;
497         var_Get( p_vout->p_sys->p_vout, "mouse-x", &valx );
498         if( valx.i_int >= (int)p_sys->posx &&
499             newval.i_int >= (int)p_sys->posy &&
500             valx.i_int <= (int)(p_sys->posx + p_sys->i_width) &&
501             newval.i_int <= (int)(p_sys->posy + p_sys->i_height) )
502         {
503             p_sys->posy = __MIN( __MAX( p_sys->posy + i_delta, 0 ),
504                           p_vout->output.i_height - p_sys->i_height );
505         }
506     }
507
508     return VLC_SUCCESS;
509 }
510
511 /*****************************************************************************
512  * Control: control facility for the vout (forwards to child vout)
513  *****************************************************************************/
514 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
515 {
516     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
517 }
518
519 /*****************************************************************************
520  * SendEventsToChild: forward events to the child/children vout
521  *****************************************************************************/
522 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
523                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
524 {
525     vout_thread_t *p_vout = (vout_thread_t *)p_this;
526     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
527     return VLC_SUCCESS;
528 }
529
530 /*****************************************************************************
531  * filter_sys_t: logo filter descriptor
532  *****************************************************************************/
533 struct filter_sys_t
534 {
535     picture_t *p_pic;
536
537     int i_width, i_height;
538     int pos, posx, posy;
539     char *psz_filename;
540     int i_trans;
541     
542     vlc_bool_t b_absolute;
543
544     mtime_t i_last_date;
545
546     /* On the fly control variable */
547     vlc_bool_t b_need_update;
548     vlc_bool_t b_new_png;
549 };
550
551 static subpicture_t *Filter( filter_t *, mtime_t );
552
553 /*****************************************************************************
554  * CreateFilter: allocates logo video filter
555  *****************************************************************************/
556 static int CreateFilter( vlc_object_t *p_this )
557 {
558     filter_t *p_filter = (filter_t *)p_this;
559     filter_sys_t *p_sys;
560     vlc_object_t *p_input;
561
562     /* Allocate structure */
563     p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
564     if( p_sys == NULL )
565     {
566         msg_Err( p_filter, "out of memory" );
567         return VLC_ENOMEM;
568     }
569
570     /* Hook used for callback variables */
571     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
572     if( !p_input )
573     {
574         free( p_sys );
575         return VLC_ENOOBJ;
576     }
577
578     p_sys->psz_filename = var_CreateGetString( p_input->p_libvlc , "logo-file" ); 
579     if( !p_sys->psz_filename || !*p_sys->psz_filename )
580     {
581         msg_Err( p_this, "logo file not specified" );
582         vlc_object_release( p_input );
583         free( p_sys );
584         return 0;
585     }
586
587     p_sys->posx = var_CreateGetInteger( p_input->p_libvlc , "logo-x" );
588     p_sys->posy = var_CreateGetInteger( p_input->p_libvlc , "logo-y" );
589     p_sys->pos = var_CreateGetInteger( p_input->p_libvlc , "logo-position" );
590     p_sys->i_trans = var_CreateGetInteger( p_input->p_libvlc, "logo-transparency");
591     p_sys->i_trans = __MAX( __MIN( p_sys->i_trans, 255 ), 0 );
592
593     var_AddCallback( p_input->p_libvlc, "logo-file", LogoCallback, p_sys );
594     var_AddCallback( p_input->p_libvlc, "logo-x", LogoCallback, p_sys );
595     var_AddCallback( p_input->p_libvlc, "logo-y", LogoCallback, p_sys );
596     var_AddCallback( p_input->p_libvlc, "logo-position", LogoCallback, p_sys );
597     var_AddCallback( p_input->p_libvlc, "logo-transparency", LogoCallback, p_sys );
598     vlc_object_release( p_input );
599
600     p_sys->b_absolute = VLC_TRUE;
601     if( p_sys->posx < 0 || p_sys->posy < 0 )
602     {
603         p_sys->b_absolute = VLC_FALSE;
604         p_sys->posx = 0; p_sys->posy = 0;
605     }
606
607     p_sys->p_pic = LoadPNG( p_this, p_sys->psz_filename, p_sys->i_trans );
608     if( !p_sys->p_pic )
609     {
610         free( p_sys );
611         return VLC_EGENERIC;
612     }
613
614     /* Misc init */
615     p_filter->pf_sub_filter = Filter;
616     p_sys->i_width = p_sys->p_pic->p[Y_PLANE].i_visible_pitch;
617     p_sys->i_height = p_sys->p_pic->p[Y_PLANE].i_visible_lines;
618     p_sys->b_need_update = VLC_TRUE;
619     p_sys->b_new_png = VLC_FALSE;
620     p_sys->i_last_date = 0;
621
622     return VLC_SUCCESS;
623 }
624
625 /*****************************************************************************
626  * DestroyFilter: destroy logo video filter
627  *****************************************************************************/
628 static void DestroyFilter( vlc_object_t *p_this )
629 {
630     filter_t *p_filter = (filter_t *)p_this;
631     filter_sys_t *p_sys = p_filter->p_sys;
632     vlc_object_t *p_input;
633
634     if( p_sys->p_pic && p_sys->p_pic->p_data_orig )
635         free( p_sys->p_pic->p_data_orig );
636     if( p_sys->p_pic ) free( p_sys->p_pic );
637
638     free( p_sys );
639     
640     /* Delete the logo variables from INPUT */
641     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
642     if( !p_input ) return;
643
644     var_Destroy( p_input->p_libvlc , "logo-file" );
645     var_Destroy( p_input->p_libvlc , "logo-x" );
646     var_Destroy( p_input->p_libvlc , "logo-y" );
647     var_Destroy( p_input->p_libvlc , "logo-position" );
648     var_Destroy( p_input->p_libvlc , "logo-transparency" );
649     vlc_object_release( p_input );
650 }
651
652 /*****************************************************************************
653  * Filter: the whole thing
654  *****************************************************************************
655  * This function outputs subpictures at regular time intervals.
656  *****************************************************************************/
657 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
658 {
659     filter_sys_t *p_sys = p_filter->p_sys;
660     subpicture_t *p_spu;
661     subpicture_region_t *p_region;
662     video_format_t fmt;
663
664     if( !p_sys->b_need_update && p_sys->i_last_date +5000000 > date ) return 0;
665
666     if( p_sys->b_new_png )
667     {
668         if( p_sys->p_pic && p_sys->p_pic->p_data_orig )
669             free( p_sys->p_pic->p_data_orig );
670         if( p_sys->p_pic ) free( p_sys->p_pic );
671
672         p_sys->p_pic = LoadPNG( VLC_OBJECT(p_filter), p_sys->psz_filename,
673                                 p_sys->i_trans );
674         if( p_sys->p_pic )
675         {
676             p_sys->i_width = p_sys->p_pic->p[Y_PLANE].i_visible_pitch;
677             p_sys->i_height = p_sys->p_pic->p[Y_PLANE].i_visible_lines;
678         }
679
680         p_sys->b_new_png = VLC_FALSE;
681     }
682
683     p_sys->b_need_update = VLC_FALSE;
684
685     /* Allocate the subpicture internal data. */
686     p_spu = p_filter->pf_sub_buffer_new( p_filter );
687     if( !p_spu ) return NULL;
688
689     p_spu->b_absolute = p_sys->b_absolute;
690     p_spu->i_start = p_sys->i_last_date = date;
691     p_spu->i_stop = 0;
692     p_spu->b_ephemer = VLC_TRUE;
693
694     p_sys->b_need_update = VLC_FALSE;
695
696     if( !p_sys->p_pic || !p_sys->i_trans )
697     {
698         /* Send an empty subpicture to clear the display */
699         return p_spu;
700     }
701
702     /* Create new SPU region */
703     memset( &fmt, 0, sizeof(video_format_t) );
704     fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
705     fmt.i_aspect = VOUT_ASPECT_FACTOR;
706     fmt.i_sar_num = fmt.i_sar_den = 1;
707     fmt.i_width = fmt.i_visible_width = p_sys->i_width;
708     fmt.i_height = fmt.i_visible_height = p_sys->i_height;
709     fmt.i_x_offset = fmt.i_y_offset = 0;
710     p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
711     if( !p_region )
712     {
713         msg_Err( p_filter, "cannot allocate SPU region" );
714         p_filter->pf_sub_buffer_del( p_filter, p_spu );
715         return NULL;
716     }
717
718     vout_CopyPicture( p_filter, &p_region->picture, p_sys->p_pic );
719     p_region->i_x = 0;
720     p_region->i_y = 0;
721     p_spu->i_x = p_sys->posx;
722     p_spu->i_y = p_sys->posy;
723     p_spu->i_flags = p_sys->pos;
724     p_spu->p_region = p_region;
725
726     return p_spu;
727 }
728
729 /*****************************************************************************
730  * Callback to update params on the fly
731  *****************************************************************************/
732 static int LogoCallback( vlc_object_t *p_this, char const *psz_var,
733                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
734 {
735     filter_sys_t *p_sys = (filter_sys_t *)p_data;
736
737     if( !strncmp( psz_var, "logo-file", 6 ) )
738     {
739         if( p_sys->psz_filename ) free( p_sys->psz_filename );
740         p_sys->psz_filename = strdup( newval.psz_string ); 
741         p_sys->b_new_png = VLC_TRUE;
742     }
743     else if ( !strncmp( psz_var, "logo-x", 6 ) )
744     {
745         p_sys->posx = newval.i_int;
746     }
747     else if ( !strncmp( psz_var, "logo-y", 6 ) )
748     {
749         p_sys->posy = newval.i_int;
750     }
751     else if ( !strncmp( psz_var, "logo-position", 12 ) )
752     {
753         p_sys->pos = newval.i_int;
754     }
755     else if ( !strncmp( psz_var, "logo-transparency", 9 ) )
756     {
757         p_sys->i_trans = __MAX( __MIN( newval.i_int, 255 ), 0 );
758         p_sys->b_new_png = VLC_TRUE;
759     }
760     p_sys->b_need_update = VLC_TRUE;
761     return VLC_SUCCESS;
762 }