logo.c 34.3 KB
Newer Older
1
2
3
/*****************************************************************************
 * logo.c : logo video plugin for vlc
 *****************************************************************************
4
 * Copyright (C) 2003-2006 the VideoLAN team
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
 *
 * 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
dionoea's avatar
dionoea committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23
24
25
26
27
28
29
30
31
 *****************************************************************************/

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

#include <vlc/vlc.h>
zorglub's avatar
zorglub committed
32
#include <vlc_vout.h>
33

34
#include "vlc_filter.h"
35
#include "filter_common.h"
36
#include "vlc_image.h"
37
#include "vlc_osd.h"
38

gbazin's avatar
gbazin committed
39
40
41
42
#ifdef LoadImage
#   undef LoadImage
#endif

43
44
45
46
47
48
49
50
51
52
53
54
/*****************************************************************************
 * 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 * );
55
static int  MouseEvent( vlc_object_t *, char const *,
56
                        vlc_value_t , vlc_value_t , void * );
57
static int  Control   ( vout_thread_t *, int, va_list );
58

59
60
61
static int  CreateFilter ( vlc_object_t * );
static void DestroyFilter( vlc_object_t * );

62
63
static int LogoCallback( vlc_object_t *, char const *,
                         vlc_value_t, vlc_value_t, void * );
64

65
66
67
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
68
#define FILE_TEXT N_("Logo filenames")
69
70
71
#define FILE_LONGTEXT N_("Full path of the image files to use. Format is " \
"<image>[,<delay in ms>[,<alpha>]][;<image>[,<delay>[,<alpha>]]][;...]. " \
"If you only have one file, simply enter its filename.")
72
#define REPEAT_TEXT N_("Logo animation # of loops")
73
#define REPEAT_LONGTEXT N_("Number of loops for the logo animation." \
Christophe Mutricy's avatar
typo    
Christophe Mutricy committed
74
        "-1 = continuous, 0 = disabled")
75
#define DELAY_TEXT N_("Logo individual image time in ms")
76
#define DELAY_LONGTEXT N_("Individual image display time of 0 - 60000 ms.")
77

78
79
80
81
82
83
#define POSX_TEXT N_("X coordinate")
#define POSX_LONGTEXT N_("X coordinate of the logo. You can move the logo " \
                "by left-clicking it." )
#define POSY_TEXT N_("Y coordinate")
#define POSY_LONGTEXT N_("Y coordinate of the logo. You can move the logo " \
                "by left-clicking it." )
84
#define TRANS_TEXT N_("Transparency of the logo")
85
#define TRANS_LONGTEXT N_("Logo transparency value " \
86
  "(from 0 for full transparency to 255 for full opacity)." )
87
88
#define POS_TEXT N_("Logo position")
#define POS_LONGTEXT N_( \
89
  "Enforce the logo position on the video " \
90
  "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
91
  "also use combinations of these values, eg 6 = top-right).")
92

93
94
#define CFG_PREFIX "logo-"

95
96
97
98
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") };
99
100

vlc_module_begin();
hartman's avatar
hartman committed
101
102
    set_description( _("Logo video filter") );
    set_capability( "video filter", 0 );
103
    set_shortname( _("Logo overlay") );
zorglub's avatar
zorglub committed
104
    set_category( CAT_VIDEO );
105
    set_subcategory( SUBCAT_VIDEO_SUBPIC );
106
107
    add_shortcut( "logo" );
    set_callbacks( Create, Destroy );
hartman's avatar
hartman committed
108

109
110
111
    add_file( CFG_PREFIX "file", NULL, NULL, FILE_TEXT, FILE_LONGTEXT, VLC_FALSE );
    add_integer( CFG_PREFIX "x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
    add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
112
    /* default to 1000 ms per image, continuously cycle through them */
113
114
115
    add_integer( CFG_PREFIX "delay", 1000, NULL, DELAY_TEXT, DELAY_LONGTEXT, VLC_TRUE );
    add_integer( CFG_PREFIX "repeat", -1, NULL, REPEAT_TEXT, REPEAT_LONGTEXT, VLC_TRUE );
    add_integer_with_range( CFG_PREFIX "transparency", 255, 0, 255, NULL,
hartman's avatar
hartman committed
116
        TRANS_TEXT, TRANS_LONGTEXT, VLC_FALSE );
117
    add_integer( CFG_PREFIX "position", 6, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
118
        change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
hartman's avatar
hartman committed
119

120
121
122
123
124
    /* subpicture filter submodule */
    add_submodule();
    set_capability( "sub filter", 0 );
    set_callbacks( CreateFilter, DestroyFilter );
    set_description( _("Logo sub filter") );
125
126
127
    add_shortcut( "logo" );
vlc_module_end();

128
129
130
131
static const char *ppsz_filter_options[] = {
    "file", "x", "y", "delay", "repeat", "transparency", "position", NULL
};

132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*****************************************************************************
 * Structure to hold the set of individual logo image names, times,
 * transparencies
 ****************************************************************************/
typedef struct
{
    char *psz_file;    /* candidate for deletion -- not needed */
    int i_delay;       /* -1 means use default delay */
    int i_alpha;       /* -1 means use default alpha */
    picture_t *p_pic;

} logo_t;

/*****************************************************************************
 * Logo list structure. Common to both the vout and sub picture filter
 ****************************************************************************/
typedef struct
{
    logo_t *p_logo;         /* the parsing's result */
    unsigned int i_count;   /* the number of logo images to be displayed */

    int i_repeat;         /* how often to repeat the images, image time in ms */
    mtime_t i_next_pic;     /* when to bring up a new logo image */

    unsigned int i_counter; /* index into the list of logo images */

    int i_delay;            /* default delay (0 - 60000 ms) */
    int i_alpha;            /* default alpha */

    char *psz_filename;     /* --logo-file string ( is it really useful
                             * to store it ? ) */

    vlc_mutex_t lock;
} logo_list_t;

167
/*****************************************************************************
168
 * LoadImage: loads the logo image into memory
169
 *****************************************************************************/
170
static picture_t *LoadImage( vlc_object_t *p_this, char *psz_filename )
171
{
172
    picture_t *p_pic;
173
174
    image_handler_t *p_image;
    video_format_t fmt_in = {0}, fmt_out = {0};
175

176
177
178
179
    fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
    p_image = image_HandlerCreate( p_this );
    p_pic = image_ReadUrl( p_image, psz_filename, &fmt_in, &fmt_out );
    image_HandlerDelete( p_image );
180
181

    return p_pic;
182
183
}

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/*****************************************************************************
 * LoadLogoList: loads the logo images into memory
 *****************************************************************************
 * Read the logo-file input switch, obtaining a list of images and associated
 * durations and transparencies.  Store the image(s), and times.  An image
 * without a stated time or transparency will use the logo-delay and
 * logo-transparency values.
 *****************************************************************************/
#define LoadLogoList( a, b ) __LoadLogoList( VLC_OBJECT( a ), b )
void __LoadLogoList( vlc_object_t *p_this, logo_list_t *p_logo_list )
{
    char *psz_list; /* the list: <logo>[,[<delay>[,[<alpha>]]]][;...] */
    unsigned int i;
    logo_t *p_logo;         /* the parsing's result */

    p_logo_list->i_counter = 0;
    p_logo_list->i_next_pic = 0;

    psz_list = strdup( p_logo_list->psz_filename );

    /* Count the number logos == number of ';' + 1 */
    p_logo_list->i_count = 1;
    for( i = 0; i < strlen( psz_list ); i++ )
    {
        if( psz_list[i] == ';' ) p_logo_list->i_count++;
    }

    p_logo_list->p_logo = p_logo =
        (logo_t *)malloc( p_logo_list->i_count * sizeof(logo_t) );

    /* Fill the data */
    for( i = 0; i < p_logo_list->i_count; i++ )
    {
        char *p_c;
        char *p_c2;
        p_c = strchr( psz_list, ';' );
        p_c2 = strchr( psz_list, ',' );

        p_logo[i].i_alpha = -1; /* use default settings */
        p_logo[i].i_delay = -1; /* use default settings */

        if( p_c2 && ( p_c2 < p_c || !p_c ) )
        {
            /* <logo>,<delay>[,<alpha>] type */
            if( p_c2[1] != ',' && p_c2[1] != ';' && p_c2[1] != '\0' )
                p_logo[i].i_delay = atoi( p_c2+1 );
            *p_c2 = '\0';
            if( ( p_c2 = strchr( p_c2+1, ',' ) )
                && ( p_c2 < p_c || !p_c ) && p_c2[1] != ';' && p_c2[1] != '\0' )
                p_logo[i].i_alpha = atoi( p_c2 + 1 );
        }
        else
        {
            /* <logo> type */
            if( p_c ) *p_c = '\0';
        }

        p_logo[i].psz_file = strdup( psz_list );
        p_logo[i].p_pic = LoadImage( p_this, p_logo[i].psz_file );

        if( !p_logo[i].p_pic )
        {
246
            msg_Warn( p_this, "error while loading logo %s, will be skipped",
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
                      p_logo[i].psz_file );
        }

        if( p_c ) psz_list = p_c + 1;
    }

    for( i = 0; i < p_logo_list->i_count; i++ )
    {
       msg_Dbg( p_this, "logo file name %s, delay %d, alpha %d",
                p_logo[i].psz_file, p_logo[i].i_delay, p_logo[i].i_alpha );
    }

    /* initialize so that on the first update it will wrap back to 0 */
    p_logo_list->i_counter = p_logo_list->i_count;
}

/*****************************************************************************
 * FreeLogoList
 *****************************************************************************/
void FreeLogoList( logo_list_t *p_logo_list )
{
    unsigned int i;
zorglub's avatar
zorglub committed
269
    FREENULL( p_logo_list->psz_filename );
270
271
272
    for( i = 0; i < p_logo_list->i_count; i++ )
    {
        logo_t *p_logo = &p_logo_list->p_logo[i];
hartman's avatar
hartman committed
273
274
        FREENULL( p_logo->psz_file );
        if( p_logo->p_pic )
275
        {
hartman's avatar
hartman committed
276
277
            p_logo->p_pic->pf_release( p_logo->p_pic );
            p_logo->p_pic = NULL;
278
279
280
281
        }
    }
}

282
/*****************************************************************************
283
 * vout_sys_t: logo video output method descriptor
284
 *****************************************************************************
285
286
287
288
289
 * This structure is part of the video output thread descriptor.
 * It describes the Invert specific properties of an output thread.
 *****************************************************************************/
struct vout_sys_t
{
290
291
    logo_list_t *p_logo_list;

292
293
294
295
296
    vout_thread_t *p_vout;

    filter_t *p_blend;

    int i_width, i_height;
297
    int pos, posx, posy;
298
299
300
301
};

/*****************************************************************************
 * Create: allocates logo video thread output method
302
303
304
305
 *****************************************************************************/
static int Create( vlc_object_t *p_this )
{
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
306
307
    vout_sys_t *p_sys;
    vlc_value_t val;
308
    logo_list_t *p_logo_list;
309
310

    /* Allocate structure */
311
312
    p_sys = p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
    if( p_sys == NULL )
313
314
315
316
    {
        msg_Err( p_vout, "out of memory" );
        return VLC_ENOMEM;
    }
317
318
319
320
321
322
323
    p_logo_list = p_sys->p_logo_list = malloc( sizeof( logo_list_t ) );
    if( p_logo_list == NULL )
    {
        msg_Err( p_vout, "out of memory" );
        free( p_sys );
        return VLC_ENOMEM;
    }
324
325
326
327
328
329

    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;
330
    p_vout->pf_control = Control;
dionoea's avatar
dionoea committed
331

332
333
    p_logo_list->psz_filename = var_CreateGetString( p_this , "logo-file" );
    if( !p_logo_list->psz_filename || !*p_logo_list->psz_filename )
334
335
336
337
    {
        msg_Err( p_this, "logo file not specified" );
        return 0;
    }
338

339
340
341
    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;
342

343
344
    var_Create( p_this, "logo-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_this, "logo-x", &val );
345
    p_sys->posx = val.i_int;
346

347
348
    var_Create( p_this, "logo-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_this, "logo-y", &val );
349
    p_sys->posy = val.i_int;
350
351
352
353
354
355
356
357
358

    var_Create( p_this, "logo-delay", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_this, "logo-delay", &val );
    p_logo_list->i_delay = __MAX( __MIN( val.i_int, 60000 ), 0 );

    var_Create( p_this, "logo-repeat", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_this, "logo-repeat", &val );
    p_logo_list->i_repeat = val.i_int;

359
360
    var_Create(p_this, "logo-transparency", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT);
    var_Get( p_this, "logo-transparency", &val );
361
    p_logo_list->i_alpha = __MAX( __MIN( val.i_int, 255 ), 0 );
362

363
    LoadLogoList( p_vout, p_logo_list );
364

365
366
367
368
369
370
371
372
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Init: initialize logo video thread output method
 *****************************************************************************/
static int Init( vout_thread_t *p_vout )
{
373
    vout_sys_t *p_sys = p_vout->p_sys;
374
    picture_t *p_pic;
375
    int i_index;
376
    video_format_t fmt = {0};
Sam Hocevar's avatar
Sam Hocevar committed
377

378
379
    logo_list_t *p_logo_list = p_sys->p_logo_list;

380
381
    I_OUTPUTPICTURES = 0;

382
383
384
385
386
    /* adjust index to the next logo */
    p_logo_list->i_counter =
                        ( p_logo_list->i_counter + 1 )%p_logo_list->i_count;

    p_pic = p_logo_list->p_logo[p_logo_list->i_counter].p_pic;
387
388
389
390
391
    /* 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;
392
393
    p_vout->fmt_out = p_vout->fmt_in;
    fmt = p_vout->fmt_out;
394

395
396
397
398
399
400
401
402
403
404
405
    /* 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;
406
407
408
409
410
411
412
413
    p_sys->i_width =
        p_sys->p_blend->fmt_in.video.i_width =
            p_sys->p_blend->fmt_in.video.i_visible_width =
                p_pic ? p_pic->p[Y_PLANE].i_visible_pitch : 0;
    p_sys->i_height =
        p_sys->p_blend->fmt_in.video.i_height =
            p_sys->p_blend->fmt_in.video.i_visible_height =
                p_pic ? p_pic->p[Y_PLANE].i_visible_lines : 0;
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
    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;
    }

431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
    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;
        }
    }

454
455
456
    /* Try to open the real video output */
    msg_Dbg( p_vout, "spawning the real video output" );

457
    p_sys->p_vout = vout_Create( p_vout, &fmt );
458
459

    /* Everything failed */
460
    if( p_sys->p_vout == NULL )
461
462
463
464
465
    {
        msg_Err( p_vout, "can't open vout, aborting" );
        return VLC_EGENERIC;
    }

466
467
    var_AddCallback( p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
    var_AddCallback( p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
468
469

    ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
470
    ADD_CALLBACKS( p_sys->p_vout, SendEvents );
gbazin's avatar
   
gbazin committed
471
472
    ADD_PARENT_CALLBACKS( SendEventsToChild );

473
474
475
476
477
478
479
480
    return VLC_SUCCESS;
}

/*****************************************************************************
 * End: terminate logo video thread output method
 *****************************************************************************/
static void End( vout_thread_t *p_vout )
{
481
    vout_sys_t *p_sys = p_vout->p_sys;
482
483
484
485
486
487
488
489
490
    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 );
    }

491
492
    var_DelCallback( p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
    var_DelCallback( p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
493

494
    if( p_sys->p_vout )
495
    {
496
497
498
        DEL_CALLBACKS( p_sys->p_vout, SendEvents );
        vlc_object_detach( p_sys->p_vout );
        vout_Destroy( p_sys->p_vout );
499
    }
500

501
502
503
504
    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 );
505
506
507
508
509
510
511
512
}

/*****************************************************************************
 * 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;
513
    vout_sys_t *p_sys = p_vout->p_sys;
514

gbazin's avatar
   
gbazin committed
515
516
    DEL_PARENT_CALLBACKS( SendEventsToChild );

517
518
519
    FreeLogoList( p_sys->p_logo_list );
    free( p_sys->p_logo_list );

520
    free( p_sys );
521
522
523
}

/*****************************************************************************
524
 * Render: render the logo onto the video
525
 *****************************************************************************/
526
static void Render( vout_thread_t *p_vout, picture_t *p_inpic )
527
{
528
    vout_sys_t *p_sys = p_vout->p_sys;
529
    picture_t *p_outpic;
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
    picture_t *p_pic;
    logo_list_t *p_logo_list;
    logo_t * p_logo;

    p_logo_list = p_sys->p_logo_list;

    if( p_logo_list->i_next_pic < p_inpic->date )
    {
        /* It's time to use a new logo */
        p_logo_list->i_counter =
                        ( p_logo_list->i_counter + 1 )%p_logo_list->i_count;
        p_logo = &p_logo_list->p_logo[p_sys->p_logo_list->i_counter];
        p_pic = p_logo->p_pic;
        p_logo_list->i_next_pic = p_inpic->date + ( p_logo->i_delay != -1 ?
                              p_logo->i_delay : p_logo_list->i_delay ) * 1000;
        if( p_pic )
        {

            p_sys->i_width =
                p_sys->p_blend->fmt_in.video.i_width =
                    p_sys->p_blend->fmt_in.video.i_visible_width =
                        p_pic->p[Y_PLANE].i_visible_pitch;
            p_sys->i_height =
                p_sys->p_blend->fmt_in.video.i_height =
                    p_sys->p_blend->fmt_in.video.i_visible_height =
                        p_pic->p[Y_PLANE].i_visible_lines;

            /* Just in case the new image would overflow the vout */
            if( (unsigned int)(p_sys->posy + p_sys->i_height)
                                                > p_vout->render.i_height
             || (unsigned int)(p_sys->posx + p_sys->i_width)
                                                > p_vout->render.i_width
             || p_sys->pos )
            {
                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;
                }
            }
        }

    }
    else
    {
        p_logo = &p_logo_list->p_logo[p_sys->p_logo_list->i_counter];
        p_pic = p_logo->p_pic;
    }
590
591

    /* This is a new frame. Get a structure from the video_output. */
592
    while( !(p_outpic = vout_CreatePicture( p_sys->p_vout, 0, 0, 0 )) )
593
    {
594
        if( p_vout->b_die || p_vout->b_error ) return;
595
596
597
        msleep( VOUT_OUTMEM_SLEEP );
    }

598
599
    vout_CopyPicture( p_vout, p_outpic, p_inpic );
    vout_DatePicture( p_sys->p_vout, p_outpic, p_inpic->date );
600

601
    if( p_pic )
602
    p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_outpic, p_outpic,
603
604
605
                                    p_pic, p_sys->posx, p_sys->posy,
                                    p_logo->i_alpha != -1 ? p_logo->i_alpha
                                    : p_logo_list->i_alpha );
606

607
    vout_DisplayPicture( p_sys->p_vout, p_outpic );
608
609
610
611
612
613
614
615
616
617
618
619
620
621
}

/*****************************************************************************
 * 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
622
 *****************************************************************************/
623
624
625
626
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;
627
    vout_sys_t *p_sys = p_vout->p_sys;
628
629
630
631
632
633
634
    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;

635
    if( (valb.i_int & 0x1) == 0 )
636
637
638
639
640
641
642
643
    {
        return VLC_SUCCESS;
    }

    if( psz_var[6] == 'x' )
    {
        vlc_value_t valy;
        var_Get( p_vout->p_sys->p_vout, "mouse-y", &valy );
644
645
646
647
        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) )
648
        {
649
650
            p_sys->posx = __MIN( __MAX( p_sys->posx + i_delta, 0 ),
                          p_vout->output.i_width - p_sys->i_width );
651
652
653
654
655
656
        }
    }
    else if( psz_var[6] == 'y' )
    {
        vlc_value_t valx;
        var_Get( p_vout->p_sys->p_vout, "mouse-x", &valx );
657
658
659
660
        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) )
661
        {
662
663
            p_sys->posy = __MIN( __MAX( p_sys->posy + i_delta, 0 ),
                          p_vout->output.i_height - p_sys->i_height );
664
665
666
667
668
        }
    }

    return VLC_SUCCESS;
}
gbazin's avatar
   
gbazin committed
669

670
671
672
673
674
675
676
677
/*****************************************************************************
 * 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
678
679
680
681
682
683
684
685
686
687
/*****************************************************************************
 * 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;
}
688
689
690
691
692
693

/*****************************************************************************
 * filter_sys_t: logo filter descriptor
 *****************************************************************************/
struct filter_sys_t
{
694
    logo_list_t *p_logo_list;
695

696
    int pos, posx, posy;
dionoea's avatar
dionoea committed
697

698
    vlc_bool_t b_absolute;
699
    mtime_t i_last_date;
700
701

    /* On the fly control variable */
702
    vlc_bool_t b_need_update;
703
704
705
706
707
708
709
710
711
712
713
};

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;
714
    logo_list_t *p_logo_list;
715

716
717
718
719
720
721
722
    /* 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;
    }
723
724
725
726
727
728
729
    p_logo_list = p_sys->p_logo_list = malloc( sizeof( logo_list_t ) );
    if( p_logo_list == NULL )
    {
        msg_Err( p_filter, "out of memory" );
        free( p_sys );
        return VLC_ENOMEM;
    }
730

731
732
733
734
    config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
                       p_filter->p_cfg );


735
    /* Hook used for callback variables */
736
    p_logo_list->psz_filename =
737
        var_CreateGetString( p_filter->p_libvlc_global , "logo-file" );
738
    if( !p_logo_list->psz_filename || !*p_logo_list->psz_filename )
739
740
    {
        msg_Err( p_this, "logo file not specified" );
741
        free( p_sys );
742
        free( p_logo_list );
743
        return VLC_EGENERIC;
744
745
    }

746
747
748
    p_sys->posx = var_CreateGetInteger( p_filter->p_libvlc_global , "logo-x" );
    p_sys->posy = var_CreateGetInteger( p_filter->p_libvlc_global , "logo-y" );
    p_sys->pos = var_CreateGetInteger( p_filter->p_libvlc_global , "logo-position" );
749
    p_logo_list->i_alpha = __MAX( __MIN( var_CreateGetInteger(
750
                           p_filter->p_libvlc_global, "logo-transparency"), 255 ), 0 );
751
    p_logo_list->i_delay =
752
                    var_CreateGetInteger( p_filter->p_libvlc_global , "logo-delay" );
753
    p_logo_list->i_repeat =
754
                    var_CreateGetInteger( p_filter->p_libvlc_global , "logo-repeat" );
755

756
757
758
759
760
761
    var_AddCallback( p_filter->p_libvlc_global, "logo-file", LogoCallback, p_sys );
    var_AddCallback( p_filter->p_libvlc_global, "logo-x", LogoCallback, p_sys );
    var_AddCallback( p_filter->p_libvlc_global, "logo-y", LogoCallback, p_sys );
    var_AddCallback( p_filter->p_libvlc_global, "logo-position", LogoCallback, p_sys );
    var_AddCallback( p_filter->p_libvlc_global, "logo-transparency", LogoCallback, p_sys );
    var_AddCallback( p_filter->p_libvlc_global, "logo-repeat", LogoCallback, p_sys );
762

763
764
765
766
767
768
    vlc_mutex_init( p_filter, &p_logo_list->lock );
    vlc_mutex_lock( &p_logo_list->lock );

    LoadLogoList( p_this, p_logo_list );

    vlc_mutex_unlock( &p_logo_list->lock );
769
770
771

    /* Misc init */
    p_filter->pf_sub_filter = Filter;
772
    p_sys->b_need_update = VLC_TRUE;
773

774
    p_sys->i_last_date = 0;
775
776
777
778
779
780
781
782
783
784
785
786

    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;

787
788
789
    vlc_mutex_destroy( &p_sys->p_logo_list->lock );
    FreeLogoList( p_sys->p_logo_list );
    free( p_sys->p_logo_list );
790
    free( p_sys );
791

792
    /* Delete the logo variables from INPUT */
793
794
795
796
797
798
799
    var_Destroy( p_filter->p_libvlc_global , "logo-file" );
    var_Destroy( p_filter->p_libvlc_global , "logo-x" );
    var_Destroy( p_filter->p_libvlc_global , "logo-y" );
    var_Destroy( p_filter->p_libvlc_global , "logo-delay" );
    var_Destroy( p_filter->p_libvlc_global , "logo-repeat" );
    var_Destroy( p_filter->p_libvlc_global , "logo-position" );
    var_Destroy( p_filter->p_libvlc_global , "logo-transparency" );
800
801
}

802
/*****************************************************************************
803
 * Filter: the whole thing
804
 *****************************************************************************
805
 * This function outputs subpictures at regular time intervals.
806
 *****************************************************************************/
807
808
809
static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
{
    filter_sys_t *p_sys = p_filter->p_sys;
810
    logo_list_t *p_logo_list = p_sys->p_logo_list;
811
812
813
    subpicture_t *p_spu;
    subpicture_region_t *p_region;
    video_format_t fmt;
814
815
    picture_t *p_pic;
    logo_t *p_logo;
816

817
818
819
820
    vlc_mutex_lock( &p_logo_list->lock );
    /* Basic test:  b_need_update occurs on a dynamic change,
                    & i_next_pic is the general timer, when to
                    look at updating the logo image */
821

822
823
    if( ( ( !p_sys->b_need_update ) && ( p_logo_list->i_next_pic > date ) )
        || !p_logo_list->i_repeat )
824
    {
825
826
        vlc_mutex_unlock( &p_logo_list->lock );
        return 0;
827
    }
828
    /* prior code tested on && p_sys->i_last_date +5000000 > date ) return 0; */
829

830
831
832
833
834
835
    /* adjust index to the next logo */
    p_logo_list->i_counter =
                        ( p_logo_list->i_counter + 1 )%p_logo_list->i_count;

    p_logo = &p_logo_list->p_logo[p_logo_list->i_counter];
    p_pic = p_logo->p_pic;
836

837
838
    /* Allocate the subpicture internal data. */
    p_spu = p_filter->pf_sub_buffer_new( p_filter );
839
840
841
842
843
    if( !p_spu )
    {
        vlc_mutex_unlock( &p_logo_list->lock );
        return NULL;
    }
844

845
846
847
848
849
850
    p_spu->b_absolute = p_sys->b_absolute;
    p_spu->i_start = p_sys->i_last_date = date;
    p_spu->i_stop = 0;
    p_spu->b_ephemer = VLC_TRUE;

    p_sys->b_need_update = VLC_FALSE;
851
852
853
854
855
856
857
858
859
860
861
862
863
    p_logo_list->i_next_pic = date +
    ( p_logo->i_delay != -1 ? p_logo->i_delay : p_logo_list->i_delay ) * 1000;

    if( p_logo_list->i_repeat != -1
        && p_logo_list->i_counter == 0 )
    {
        p_logo_list->i_repeat--;
        if( p_logo_list->i_repeat == 0 )
        {
            vlc_mutex_unlock( &p_logo_list->lock );
            return p_spu;
        }
    }
864

865
866
    if( !p_pic || !p_logo->i_alpha
        || ( p_logo->i_alpha == -1 && !p_logo_list->i_alpha ) )
867
868
    {
        /* Send an empty subpicture to clear the display */
869
        vlc_mutex_unlock( &p_logo_list->lock );
870
871
872
        return p_spu;
    }

873
874
875
876
    /* 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;
877
    fmt.i_sar_num = fmt.i_sar_den = 1;
878
879
    fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
    fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
880
881
882
883
884
885
    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 );
886
        vlc_mutex_unlock( &p_logo_list->lock );
887
888
        return NULL;
    }
889

890
891
    vout_CopyPicture( p_filter, &p_region->picture, p_pic );
    vlc_mutex_unlock( &p_logo_list->lock );
892
893
894
895
896
897
898
899
900
901
902

    /*  where to locate the logo: */
    if( p_sys->posx < 0 || p_sys->posy < 0 )
    {   /* set to one of the 9 relative locations */
        p_spu->i_flags = p_sys->pos;
        p_spu->i_x = 0;
        p_spu->i_y = 0;
        p_spu->b_absolute = VLC_FALSE;
    }
    else
    {   /*  set to an absolute xy, referenced to upper left corner */
dionoea's avatar
dionoea committed
903
        p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
904
905
906
907
908
        p_spu->i_x = p_sys->posx;
        p_spu->i_y = p_sys->posy;
        p_spu->b_absolute = VLC_TRUE;
    }

909
    p_spu->p_region = p_region;
910
911
912

    p_spu->i_alpha = ( p_logo->i_alpha != -1 ?
                       p_logo->i_alpha : p_logo_list->i_alpha );
913
914
915

    return p_spu;
}
916
917

/*****************************************************************************
918
 * Callback to update params on the fly
919
 *****************************************************************************/
920
static int LogoCallback( vlc_object_t *p_this, char const *psz_var,
921
                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
922
{
923
    filter_sys_t *p_sys = (filter_sys_t *)p_data;
924
    logo_list_t *p_logo_list = p_sys->p_logo_list;
925

926
    if( !strncmp( psz_var, "logo-file", 6 ) )
927
    {
928
929
930
931
932
933
        vlc_mutex_lock( &p_logo_list->lock );
        FreeLogoList( p_logo_list );
        p_logo_list->psz_filename = strdup( newval.psz_string );
        LoadLogoList( p_this, p_logo_list );
        vlc_mutex_unlock( &p_logo_list->lock );
        p_sys->b_need_update = VLC_TRUE;
934
    }
935
    else if ( !strncmp( psz_var, "logo-x", 6 ) )
936
937
938
939
940
941
942
943
944
945
946
    {
        p_sys->posx = newval.i_int;
    }
    else if ( !strncmp( psz_var, "logo-y", 6 ) )
    {
        p_sys->posy = newval.i_int;
    }
    else if ( !strncmp( psz_var, "logo-position", 12 ) )
    {
        p_sys->pos = newval.i_int;
    }
947
948
    else if ( !strncmp( psz_var, "logo-transparency", 9 ) )
    {
949
950
951
952
953
954
955
956
957
        vlc_mutex_lock( &p_logo_list->lock );
        p_logo_list->i_alpha = __MAX( __MIN( newval.i_int, 255 ), 0 );
        vlc_mutex_unlock( &p_logo_list->lock );
    }
    else if ( !strncmp( psz_var, "logo-repeat", 11 ) )
    {
        vlc_mutex_lock( &p_logo_list->lock );
        p_logo_list->i_repeat = newval.i_int;
        vlc_mutex_unlock( &p_logo_list->lock );
958
    }
959
960
961
    p_sys->b_need_update = VLC_TRUE;
    return VLC_SUCCESS;
}