logo.c 22.8 KB
Newer Older
1
2
3
/*****************************************************************************
 * logo.c : logo video plugin for vlc
 *****************************************************************************
hartman's avatar
hartman committed
4
 * Copyright (C) 2003-2004 VideoLAN
5
 * $Id$
6
 *
7
8
 * Authors: Gildas Bazin <gbazin@videolan.org>
 *          Simon Latapie <garf@videolan.org>
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */
#include <string.h>

#include <png.h>

#include <vlc/vlc.h>
#include <vlc/vout.h>

36
#include "vlc_filter.h"
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include "filter_common.h"

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int  Create    ( vlc_object_t * );
static void Destroy   ( vlc_object_t * );

static int  Init      ( vout_thread_t * );
static void End       ( vout_thread_t * );
static void Render    ( vout_thread_t *, picture_t * );

static int  SendEvents( vlc_object_t *, char const *,
                        vlc_value_t, vlc_value_t, void * );
51
52
53
static int MouseEvent ( vlc_object_t *, char const *,
                        vlc_value_t , vlc_value_t , void * );
static int Control    ( vout_thread_t *, int, va_list );
54

55
56
57
static int  CreateFilter ( vlc_object_t * );
static void DestroyFilter( vlc_object_t * );

58
59
60
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Sam Hocevar's avatar
Sam Hocevar committed
61
#define FILE_TEXT N_("Logo filename")
62
#define FILE_LONGTEXT N_("Full path of the PNG file to use.")
Sam Hocevar's avatar
Sam Hocevar committed
63
#define POSX_TEXT N_("X coordinate of the logo")
64
#define POSX_LONGTEXT N_("You can move the logo by left-clicking on it." )
Sam Hocevar's avatar
Sam Hocevar committed
65
#define POSY_TEXT N_("Y coordinate of the logo")
66
#define POSY_LONGTEXT N_("You can move the logo by left-clicking on it." )
67
68
69
#define TRANS_TEXT N_("Transparency of the logo")
#define TRANS_LONGTEXT N_("You can set the logo transparency value here " \
  "(from 0 for full transparency to 255 for full opacity)." )
70
71
72
73
74
75
76
77
78
79
#define POS_TEXT N_("Logo position")
#define POS_LONGTEXT N_( \
  "You can enforce the logo position on the video " \
  "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
  "also use combinations of these values).")

static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
static char *ppsz_pos_descriptions[] =
{ N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
  N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
80
81

vlc_module_begin();
hartman's avatar
hartman committed
82
83
    set_description( _("Logo video filter") );
    set_capability( "video filter", 0 );
84
85
    add_shortcut( "logo" );
    set_callbacks( Create, Destroy );
hartman's avatar
hartman committed
86

sigmunau's avatar
sigmunau committed
87
    add_file( "logo-file", NULL, NULL, FILE_TEXT, FILE_LONGTEXT, VLC_FALSE );
88
89
    add_integer( "logo-x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_FALSE );
    add_integer( "logo-y", -1, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_FALSE );
hartman's avatar
hartman committed
90
91
    add_integer_with_range( "logo-transparency", 255, 0, 255, NULL,
        TRANS_TEXT, TRANS_LONGTEXT, VLC_FALSE );
92
93
    add_integer( "logo-position", 6, NULL, POS_TEXT, POS_LONGTEXT, VLC_TRUE );
        change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
hartman's avatar
hartman committed
94

95
96
97
98
99
    /* subpicture filter submodule */
    add_submodule();
    set_capability( "sub filter", 0 );
    set_callbacks( CreateFilter, DestroyFilter );
    set_description( _("Logo sub filter") );
100
101
102
    add_shortcut( "logo" );
vlc_module_end();

103
/*****************************************************************************
104
 * LoadPNG: loads the PNG logo into memory
105
 *****************************************************************************/
106
static picture_t *LoadPNG( vlc_object_t *p_this )
107
{
108
109
110
111
112
    picture_t *p_pic;
    char *psz_filename;
    vlc_value_t val;
    FILE *file;
    int i, j, i_trans;
113
    vlc_bool_t b_alpha = VLC_TRUE;
114
115
116
117
118
119
120
121
122
123
124

    png_uint_32 i_width, i_height;
    int i_color_type, i_interlace_type, i_compression_type, i_filter_type;
    int i_bit_depth;
    png_bytep *p_row_pointers;
    png_structp p_png;
    png_infop p_info, p_end_info;

    var_Create( p_this, "logo-file", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
    var_Get( p_this, "logo-file", &val );
    psz_filename = val.psz_string;
125
126
127
128
129
    if( !psz_filename || !*psz_filename )
    {
        msg_Err( p_this, "logo file not specified" );
        return 0;
    }
130
131
132

    if( !(file = fopen( psz_filename , "rb" )) )
    {
133
        msg_Err( p_this, "logo file (%s) not found", psz_filename );
134
135
136
137
138
139
140
141
142
143
144
145
146
147
        free( psz_filename );
        return 0;
    }
    free( psz_filename );

    p_png = png_create_read_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 );
    p_info = png_create_info_struct( p_png );
    p_end_info = png_create_info_struct( p_png );
    png_init_io( p_png, file );
    png_read_info( p_png, p_info );
    png_get_IHDR( p_png, p_info, &i_width, &i_height,
                  &i_bit_depth, &i_color_type, &i_interlace_type,
                  &i_compression_type, &i_filter_type);

148
149
150
151
152
153
154
155
    if( i_color_type == PNG_COLOR_TYPE_PALETTE )
        png_set_palette_to_rgb( p_png );

    if( i_color_type == PNG_COLOR_TYPE_GRAY ||
        i_color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
          png_set_gray_to_rgb( p_png );

    if( png_get_valid( p_png, p_info, PNG_INFO_tRNS ) )
156
    {
157
        png_set_tRNS_to_alpha( p_png );
158
    }
159
    else if( !(i_color_type & PNG_COLOR_MASK_ALPHA) )
160
161
162
    {
        b_alpha = VLC_FALSE;
    }
163

164
165
166
    p_row_pointers = malloc( sizeof(png_bytep) * i_height );
    for( i = 0; i < (int)i_height; i++ )
        p_row_pointers[i] = malloc( 4 * ( i_bit_depth + 7 ) / 8 * i_width );
167

168
169
170
171
172
173
174
175
176
177
178
179
    png_read_image( p_png, p_row_pointers );
    png_read_end( p_png, p_end_info );

    fclose( file );
    png_destroy_read_struct( &p_png, &p_info, &p_end_info );

    /* Convert to YUVA */
    p_pic = malloc( sizeof(picture_t) );
    if( vout_AllocatePicture( p_this, p_pic, VLC_FOURCC('Y','U','V','A'),
                              i_width, i_height, VOUT_ASPECT_FACTOR ) !=
        VLC_SUCCESS )
    {
180
        for( i = 0; i < (int)i_height; i++ ) free( p_row_pointers[i] );
181
182
183
184
185
186
        free( p_row_pointers );
        return 0;
    }

    var_Create(p_this, "logo-transparency", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT);
    var_Get( p_this, "logo-transparency", &val );
187
    i_trans = __MAX( __MIN( val.i_int, 255 ), 0 );
188
189
190

    for( j = 0; j < (int)i_height ; j++ )
    {
191
192
        uint8_t *p = (uint8_t *)p_row_pointers[j];

193
194
195
196
197
        for( i = 0; i < (int)i_width ; i++ )
        {
            int i_offset = i + j * p_pic->p[Y_PLANE].i_pitch;

            p_pic->p[Y_PLANE].p_pixels[i_offset] =
198
                (p[0] * 257L + p[1] * 504 + p[2] * 98)/1000 + 16;
199
            p_pic->p[U_PLANE].p_pixels[i_offset] =
200
                (p[2] * 439L - p[0] * 148 - p[1] * 291)/1000 + 128;
201
            p_pic->p[V_PLANE].p_pixels[i_offset] =
202
203
                (p[0] * 439L - p[1] * 368 - p[2] * 71)/1000 + 128;
            p_pic->p[A_PLANE].p_pixels[i_offset] =
204
                b_alpha ? (p[3] * i_trans) / 255 : i_trans;
205
206

            p += (b_alpha ? 4 : 3);
207
208
209
        }
    }

210
    for( i = 0; i < (int)i_height; i++ ) free( p_row_pointers[i] );
211
212
    free( p_row_pointers );
    return p_pic;
213
214
}

215
/*****************************************************************************
216
 * vout_sys_t: logo video output method descriptor
217
 *****************************************************************************
218
219
220
221
222
223
224
225
226
227
228
 * This structure is part of the video output thread descriptor.
 * It describes the Invert specific properties of an output thread.
 *****************************************************************************/
struct vout_sys_t
{
    vout_thread_t *p_vout;

    filter_t *p_blend;
    picture_t *p_pic;

    int i_width, i_height;
229
    int pos, posx, posy;
230
231
232
233
};

/*****************************************************************************
 * Create: allocates logo video thread output method
234
235
236
237
 *****************************************************************************/
static int Create( vlc_object_t *p_this )
{
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
238
239
    vout_sys_t *p_sys;
    vlc_value_t val;
240
241

    /* Allocate structure */
242
243
    p_sys = p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
    if( p_sys == NULL )
244
245
246
247
248
249
250
251
252
253
    {
        msg_Err( p_vout, "out of memory" );
        return VLC_ENOMEM;
    }

    p_vout->pf_init = Init;
    p_vout->pf_end = End;
    p_vout->pf_manage = NULL;
    p_vout->pf_render = Render;
    p_vout->pf_display = NULL;
254
    p_vout->pf_control = Control;
255

256
257
258
    var_Create( p_this, "logo-position", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_this, "logo-position", &val );
    p_sys->pos = val.i_int;
259
260
    var_Create( p_this, "logo-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_this, "logo-x", &val );
261
    p_sys->posx = val.i_int;
262
263
    var_Create( p_this, "logo-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_this, "logo-y", &val );
264
    p_sys->posy = val.i_int;
265
266
267
268
269
270
271
272
273
274
275

    p_sys->p_pic = LoadPNG( p_this );
    if( !p_sys->p_pic )
    {
        free( p_sys );
        return VLC_EGENERIC;
    }

    p_sys->i_width = p_sys->p_pic->p[Y_PLANE].i_visible_pitch;
    p_sys->i_height = p_sys->p_pic->p[Y_PLANE].i_visible_lines;

276
277
278
279
280
281
282
283
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Init: initialize logo video thread output method
 *****************************************************************************/
static int Init( vout_thread_t *p_vout )
{
284
    vout_sys_t *p_sys = p_vout->p_sys;
285
    picture_t *p_pic;
286
    int i_index;
Sam Hocevar's avatar
Sam Hocevar committed
287

288
289
290
291
292
293
294
295
    I_OUTPUTPICTURES = 0;

    /* Initialize the output structure */
    p_vout->output.i_chroma = p_vout->render.i_chroma;
    p_vout->output.i_width  = p_vout->render.i_width;
    p_vout->output.i_height = p_vout->render.i_height;
    p_vout->output.i_aspect = p_vout->render.i_aspect;

296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
    /* Load the video blending filter */
    p_sys->p_blend = vlc_object_create( p_vout, sizeof(filter_t) );
    vlc_object_attach( p_sys->p_blend, p_vout );
    p_sys->p_blend->fmt_out.video.i_x_offset =
        p_sys->p_blend->fmt_out.video.i_y_offset = 0;
    p_sys->p_blend->fmt_in.video.i_x_offset =
        p_sys->p_blend->fmt_in.video.i_y_offset = 0;
    p_sys->p_blend->fmt_out.video.i_aspect = p_vout->render.i_aspect;
    p_sys->p_blend->fmt_out.video.i_chroma = p_vout->output.i_chroma;
    p_sys->p_blend->fmt_in.video.i_chroma = VLC_FOURCC('Y','U','V','A');
    p_sys->p_blend->fmt_in.video.i_aspect = VOUT_ASPECT_FACTOR;
    p_sys->p_blend->fmt_in.video.i_width =
        p_sys->p_blend->fmt_in.video.i_visible_width =
            p_sys->p_pic->p[Y_PLANE].i_visible_pitch;
    p_sys->p_blend->fmt_in.video.i_height =
        p_sys->p_blend->fmt_in.video.i_visible_height =
            p_sys->p_pic->p[Y_PLANE].i_visible_lines;
    p_sys->p_blend->fmt_out.video.i_width =
        p_sys->p_blend->fmt_out.video.i_visible_width =
           p_vout->output.i_width;
    p_sys->p_blend->fmt_out.video.i_height =
        p_sys->p_blend->fmt_out.video.i_visible_height =
            p_vout->output.i_height;

    p_sys->p_blend->p_module =
        module_Need( p_sys->p_blend, "video blending", 0, 0 );
    if( !p_sys->p_blend->p_module )
    {
        msg_Err( p_vout, "can't open blending filter, aborting" );
        vlc_object_detach( p_sys->p_blend );
        vlc_object_destroy( p_sys->p_blend );
        return VLC_EGENERIC;
    }

330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
    if( p_sys->posx < 0 || p_sys->posy < 0 )
    {
        p_sys->posx = 0; p_sys->posy = 0;

        if( p_sys->pos & SUBPICTURE_ALIGN_BOTTOM )
        {
            p_sys->posy = p_vout->render.i_height - p_sys->i_height;
        }
        else if ( !(p_sys->pos & SUBPICTURE_ALIGN_TOP) )
        {
            p_sys->posy = p_vout->render.i_height / 2 - p_sys->i_height / 2;
        }

        if( p_sys->pos & SUBPICTURE_ALIGN_RIGHT )
        {
            p_sys->posx = p_vout->render.i_width - p_sys->i_width;
        }
        else if ( !(p_sys->pos & SUBPICTURE_ALIGN_LEFT) )
        {
            p_sys->posx = p_vout->render.i_width / 2 - p_sys->i_width / 2;
        }
    }

353
354
355
    /* Try to open the real video output */
    msg_Dbg( p_vout, "spawning the real video output" );

356
357
358
    p_sys->p_vout =
        vout_Create( p_vout, p_vout->render.i_width, p_vout->render.i_height,
                     p_vout->render.i_chroma, p_vout->render.i_aspect );
359
360

    /* Everything failed */
361
    if( p_sys->p_vout == NULL )
362
363
364
365
366
    {
        msg_Err( p_vout, "can't open vout, aborting" );
        return VLC_EGENERIC;
    }

367
368
    var_AddCallback( p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
    var_AddCallback( p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
369
370

    ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
371
    ADD_CALLBACKS( p_sys->p_vout, SendEvents );
gbazin's avatar
   
gbazin committed
372
373
    ADD_PARENT_CALLBACKS( SendEventsToChild );

374
375
376
377
378
379
380
381
    return VLC_SUCCESS;
}

/*****************************************************************************
 * End: terminate logo video thread output method
 *****************************************************************************/
static void End( vout_thread_t *p_vout )
{
382
    vout_sys_t *p_sys = p_vout->p_sys;
383
384
385
386
387
388
389
390
391
    int i_index;

    /* Free the fake output buffers we allocated */
    for( i_index = I_OUTPUTPICTURES ; i_index ; )
    {
        i_index--;
        free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
    }

392
393
    var_DelCallback( p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
    var_DelCallback( p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
394

395
    if( p_sys->p_vout )
396
    {
397
398
399
        DEL_CALLBACKS( p_sys->p_vout, SendEvents );
        vlc_object_detach( p_sys->p_vout );
        vout_Destroy( p_sys->p_vout );
400
    }
401

402
403
404
405
    if( p_sys->p_blend->p_module )
        module_Unneed( p_sys->p_blend, p_sys->p_blend->p_module );
    vlc_object_detach( p_sys->p_blend );
    vlc_object_destroy( p_sys->p_blend );
406
407
408
409
410
411
412
413
}

/*****************************************************************************
 * Destroy: destroy logo video thread output method
 *****************************************************************************/
static void Destroy( vlc_object_t *p_this )
{
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
414
    vout_sys_t *p_sys = p_vout->p_sys;
415

gbazin's avatar
   
gbazin committed
416
417
    DEL_PARENT_CALLBACKS( SendEventsToChild );

418
419
420
421
422
    if( p_sys->p_pic && p_sys->p_pic->p_data_orig )
        free( p_sys->p_pic->p_data_orig );
    if( p_sys->p_pic ) free( p_sys->p_pic );

    free( p_sys );
423
424
425
}

/*****************************************************************************
426
 * Render: render the logo onto the video
427
428
429
 *****************************************************************************/
static void Render( vout_thread_t *p_vout, picture_t *p_pic )
{
430
    vout_sys_t *p_sys = p_vout->p_sys;
431
432
433
    picture_t *p_outpic;

    /* This is a new frame. Get a structure from the video_output. */
434
    while( !(p_outpic = vout_CreatePicture( p_sys->p_vout, 0, 0, 0 )) )
435
    {
436
        if( p_vout->b_die || p_vout->b_error ) return;
437
438
439
        msleep( VOUT_OUTMEM_SLEEP );
    }

440
441
    vout_CopyPicture( p_vout, p_outpic, p_pic );
    vout_DatePicture( p_sys->p_vout, p_outpic, p_pic->date );
442

443
444
    p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_outpic, p_outpic,
                                    p_sys->p_pic, p_sys->posx, p_sys->posy );
445

446
    vout_DisplayPicture( p_sys->p_vout, p_outpic );
447
448
449
450
451
452
453
454
455
456
457
458
459
460
}

/*****************************************************************************
 * SendEvents: forward mouse and keyboard events to the parent p_vout
 *****************************************************************************/
static int SendEvents( vlc_object_t *p_this, char const *psz_var,
                       vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    var_Set( (vlc_object_t *)p_data, psz_var, newval );
    return VLC_SUCCESS;
}

/*****************************************************************************
 * MouseEvent: callback for mouse events
461
 *****************************************************************************/
462
463
464
465
static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
                       vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    vout_thread_t *p_vout = (vout_thread_t*)p_data;
466
    vout_sys_t *p_sys = p_vout->p_sys;
467
468
469
470
471
472
473
    vlc_value_t valb;
    int i_delta;

    var_Get( p_vout->p_sys->p_vout, "mouse-button-down", &valb );

    i_delta = newval.i_int - oldval.i_int;

474
    if( (valb.i_int & 0x1) == 0 )
475
476
477
478
479
480
481
482
    {
        return VLC_SUCCESS;
    }

    if( psz_var[6] == 'x' )
    {
        vlc_value_t valy;
        var_Get( p_vout->p_sys->p_vout, "mouse-y", &valy );
483
484
485
486
        if( newval.i_int >= (int)p_sys->posx &&
            valy.i_int >= (int)p_sys->posy &&
            newval.i_int <= (int)(p_sys->posx + p_sys->i_width) &&
            valy.i_int <= (int)(p_sys->posy + p_sys->i_height) )
487
        {
488
489
            p_sys->posx = __MIN( __MAX( p_sys->posx + i_delta, 0 ),
                          p_vout->output.i_width - p_sys->i_width );
490
491
492
493
494
495
        }
    }
    else if( psz_var[6] == 'y' )
    {
        vlc_value_t valx;
        var_Get( p_vout->p_sys->p_vout, "mouse-x", &valx );
496
497
498
499
        if( valx.i_int >= (int)p_sys->posx &&
            newval.i_int >= (int)p_sys->posy &&
            valx.i_int <= (int)(p_sys->posx + p_sys->i_width) &&
            newval.i_int <= (int)(p_sys->posy + p_sys->i_height) )
500
        {
501
502
            p_sys->posy = __MIN( __MAX( p_sys->posy + i_delta, 0 ),
                          p_vout->output.i_height - p_sys->i_height );
503
504
505
506
507
        }
    }

    return VLC_SUCCESS;
}
gbazin's avatar
   
gbazin committed
508

509
510
511
512
513
514
515
516
/*****************************************************************************
 * Control: control facility for the vout (forwards to child vout)
 *****************************************************************************/
static int Control( vout_thread_t *p_vout, int i_query, va_list args )
{
    return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
}

gbazin's avatar
   
gbazin committed
517
518
519
520
521
522
523
524
525
526
/*****************************************************************************
 * SendEventsToChild: forward events to the child/children vout
 *****************************************************************************/
static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
                       vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
    var_Set( p_vout->p_sys->p_vout, psz_var, newval );
    return VLC_SUCCESS;
}
527
528
529
530
531
532
533
534
535

/*****************************************************************************
 * filter_sys_t: logo filter descriptor
 *****************************************************************************/
struct filter_sys_t
{
    picture_t *p_pic;

    int i_width, i_height;
536
537
538
    int pos, posx, posy;

    vlc_bool_t b_absolute;
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561

    mtime_t i_last_date;
};

static subpicture_t *Filter( filter_t *, mtime_t );

/*****************************************************************************
 * CreateFilter: allocates logo video filter
 *****************************************************************************/
static int CreateFilter( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t *)p_this;
    filter_sys_t *p_sys;
    vlc_value_t val;

    /* Allocate structure */
    p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
    if( p_sys == NULL )
    {
        msg_Err( p_filter, "out of memory" );
        return VLC_ENOMEM;
    }

562
563
564
    var_Create( p_this, "logo-position", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_this, "logo-position", &val );
    p_sys->pos = val.i_int;
565
566
567
568
569
570
571
    var_Create( p_this, "logo-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_this, "logo-x", &val );
    p_sys->posx = val.i_int;
    var_Create( p_this, "logo-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_this, "logo-y", &val );
    p_sys->posy = val.i_int;

572
573
574
575
576
577
578
    p_sys->b_absolute = VLC_TRUE;
    if( p_sys->posx < 0 || p_sys->posy < 0 )
    {
        p_sys->b_absolute = VLC_FALSE;
        p_sys->posx = 0; p_sys->posy = 0;
    }

579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
    p_sys->p_pic = LoadPNG( p_this );
    if( !p_sys->p_pic )
    {
        free( p_sys );
        return VLC_EGENERIC;
    }

    p_sys->i_width = p_sys->p_pic->p[Y_PLANE].i_visible_pitch;
    p_sys->i_height = p_sys->p_pic->p[Y_PLANE].i_visible_lines;
    p_sys->i_last_date = 0;

    /* Misc init */
    p_filter->pf_sub_filter = Filter;

    return VLC_SUCCESS;
}

/*****************************************************************************
 * DestroyFilter: destroy logo video filter
 *****************************************************************************/
static void DestroyFilter( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t *)p_this;
    filter_sys_t *p_sys = p_filter->p_sys;

    if( p_sys->p_pic && p_sys->p_pic->p_data_orig )
        free( p_sys->p_pic->p_data_orig );
    if( p_sys->p_pic ) free( p_sys->p_pic );

    free( p_sys );
}

/****************************************************************************
 * Filter: the whole thing
 ****************************************************************************
 * This function outputs subpictures at regular time intervals.
 ****************************************************************************/
static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
{
    filter_sys_t *p_sys = p_filter->p_sys;
    subpicture_t *p_spu;
    subpicture_region_t *p_region;
    video_format_t fmt;

623
    if( p_sys->i_last_date && p_sys->i_last_date + 5000000 > date ) return 0;
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648

    /* Allocate the subpicture internal data. */
    p_spu = p_filter->pf_sub_buffer_new( p_filter );
    if( !p_spu ) return NULL;

    /* Create new SPU region */
    memset( &fmt, 0, sizeof(video_format_t) );
    fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
    fmt.i_aspect = VOUT_ASPECT_FACTOR;
    fmt.i_width = fmt.i_visible_width = p_sys->i_width;
    fmt.i_height = fmt.i_visible_height = p_sys->i_height;
    fmt.i_x_offset = fmt.i_y_offset = 0;
    p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
    if( !p_region )
    {
        msg_Err( p_filter, "cannot allocate SPU region" );
        p_filter->pf_sub_buffer_del( p_filter, p_spu );
        return NULL;
    }

    vout_CopyPicture( p_filter, &p_region->picture, p_sys->p_pic );
    p_region->i_x = 0;
    p_region->i_y = 0;
    p_spu->i_x = p_sys->posx;
    p_spu->i_y = p_sys->posy;
649
650
651
    p_spu->i_flags = p_sys->pos;
    p_spu->b_absolute = p_sys->b_absolute;

652
653
    p_spu->p_region = p_region;

654
    p_spu->i_start = p_sys->i_last_date = date;
655
656
657
658
659
    p_spu->i_stop = 0;
    p_spu->b_ephemer = VLC_TRUE;

    return p_spu;
}