logo.c 34.4 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
32
33
 *****************************************************************************/

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

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

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
74
#define REPEAT_LONGTEXT N_("Number of loops for the logo animation." \
        "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
95
96

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") };
97
98

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

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

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

126
127
128
129
130
131
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
/*****************************************************************************
 * 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;

161
/*****************************************************************************
162
 * LoadImage: loads the logo image into memory
163
 *****************************************************************************/
164
static picture_t *LoadImage( vlc_object_t *p_this, char *psz_filename )
165
{
166
    picture_t *p_pic;
167
168
    image_handler_t *p_image;
    video_format_t fmt_in = {0}, fmt_out = {0};
169

170
171
172
173
    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 );
174
175

    return p_pic;
176
177
}

178
179
180
181
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
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/*****************************************************************************
 * 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 )
        {
            msg_Warn( p_this, "Error while loading logo %s. It will be skipped",
                      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
 *****************************************************************************/
#define FREE( a ) free(a);a=NULL;
void FreeLogoList( logo_list_t *p_logo_list )
{
    unsigned int i;
    if( p_logo_list->psz_filename ) FREE( p_logo_list->psz_filename );
    for( i = 0; i < p_logo_list->i_count; i++ )
    {
        logo_t *p_logo = &p_logo_list->p_logo[i];
        if( p_logo[i].psz_file ) FREE( p_logo[i].psz_file );
        if( p_logo[i].p_pic )
        {
            p_logo[i].p_pic->pf_release( p_logo[i].p_pic );
            p_logo[i].p_pic = NULL;
        }
    }
}
#undef FREE

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

288
289
290
291
292
    vout_thread_t *p_vout;

    filter_t *p_blend;

    int i_width, i_height;
293
    int pos, posx, posy;
294
295
296
297
};

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

    /* Allocate structure */
307
308
    p_sys = p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
    if( p_sys == NULL )
309
310
311
312
    {
        msg_Err( p_vout, "out of memory" );
        return VLC_ENOMEM;
    }
313
314
315
316
317
318
319
    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;
    }
320
321
322
323
324
325

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

328
329
    p_logo_list->psz_filename = var_CreateGetString( p_this , "logo-file" );
    if( !p_logo_list->psz_filename || !*p_logo_list->psz_filename )
330
331
332
333
    {
        msg_Err( p_this, "logo file not specified" );
        return 0;
    }
334

335
336
337
    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;
338

339
340
    var_Create( p_this, "logo-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_this, "logo-x", &val );
341
    p_sys->posx = val.i_int;
342

343
344
    var_Create( p_this, "logo-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_this, "logo-y", &val );
345
    p_sys->posy = val.i_int;
346
347
348
349
350
351
352
353
354

    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;

355
356
    var_Create(p_this, "logo-transparency", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT);
    var_Get( p_this, "logo-transparency", &val );
357
    p_logo_list->i_alpha = __MAX( __MIN( val.i_int, 255 ), 0 );
358

359
    LoadLogoList( p_vout, p_logo_list );
360

361
362
363
364
365
366
367
368
    return VLC_SUCCESS;
}

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

374
375
    logo_list_t *p_logo_list = p_sys->p_logo_list;

376
377
    I_OUTPUTPICTURES = 0;

378
379
380
381
382
    /* 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;
383
384
385
386
387
    /* 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;
388
389
    p_vout->fmt_out = p_vout->fmt_in;
    fmt = p_vout->fmt_out;
390

391
392
393
394
395
396
397
398
399
400
401
    /* 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;
402
403
404
405
406
407
408
409
    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;
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
    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;
    }

427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
    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;
        }
    }

450
451
452
    /* Try to open the real video output */
    msg_Dbg( p_vout, "spawning the real video output" );

453
    p_sys->p_vout = vout_Create( p_vout, &fmt );
454
455

    /* Everything failed */
456
    if( p_sys->p_vout == NULL )
457
458
459
460
461
    {
        msg_Err( p_vout, "can't open vout, aborting" );
        return VLC_EGENERIC;
    }

462
463
    var_AddCallback( p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
    var_AddCallback( p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
464
465

    ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
466
    ADD_CALLBACKS( p_sys->p_vout, SendEvents );
gbazin's avatar
   
gbazin committed
467
468
    ADD_PARENT_CALLBACKS( SendEventsToChild );

469
470
471
472
473
474
475
476
    return VLC_SUCCESS;
}

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

487
488
    var_DelCallback( p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
    var_DelCallback( p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
489

490
    if( p_sys->p_vout )
491
    {
492
493
494
        DEL_CALLBACKS( p_sys->p_vout, SendEvents );
        vlc_object_detach( p_sys->p_vout );
        vout_Destroy( p_sys->p_vout );
495
    }
496

497
498
499
500
    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 );
501
502
503
504
505
506
507
508
}

/*****************************************************************************
 * 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;
509
    vout_sys_t *p_sys = p_vout->p_sys;
510

gbazin's avatar
   
gbazin committed
511
512
    DEL_PARENT_CALLBACKS( SendEventsToChild );

513
514
515
    FreeLogoList( p_sys->p_logo_list );
    free( p_sys->p_logo_list );

516
    free( p_sys );
517
518
519
}

/*****************************************************************************
520
 * Render: render the logo onto the video
521
 *****************************************************************************/
522
static void Render( vout_thread_t *p_vout, picture_t *p_inpic )
523
{
524
    vout_sys_t *p_sys = p_vout->p_sys;
525
    picture_t *p_outpic;
526
527
528
529
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
    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;
    }
586
587

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

594
595
    vout_CopyPicture( p_vout, p_outpic, p_inpic );
    vout_DatePicture( p_sys->p_vout, p_outpic, p_inpic->date );
596

597
    if( p_pic )
598
    p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_outpic, p_outpic,
599
600
601
                                    p_pic, p_sys->posx, p_sys->posy,
                                    p_logo->i_alpha != -1 ? p_logo->i_alpha
                                    : p_logo_list->i_alpha );
602

603
    vout_DisplayPicture( p_sys->p_vout, p_outpic );
604
605
606
607
608
609
610
611
612
613
614
615
616
617
}

/*****************************************************************************
 * 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
618
 *****************************************************************************/
619
620
621
622
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;
623
    vout_sys_t *p_sys = p_vout->p_sys;
624
625
626
627
628
629
630
    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;

631
    if( (valb.i_int & 0x1) == 0 )
632
633
634
635
636
637
638
639
    {
        return VLC_SUCCESS;
    }

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

    return VLC_SUCCESS;
}
gbazin's avatar
   
gbazin committed
665

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

/*****************************************************************************
 * filter_sys_t: logo filter descriptor
 *****************************************************************************/
struct filter_sys_t
{
690
    logo_list_t *p_logo_list;
691

692
    int pos, posx, posy;
dionoea's avatar
dionoea committed
693

694
    vlc_bool_t b_absolute;
695
    mtime_t i_last_date;
696
697

    /* On the fly control variable */
698
    vlc_bool_t b_need_update;
699
700
701
702
703
704
705
706
707
708
709
};

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;
710
    vlc_object_t *p_input;
711
    logo_list_t *p_logo_list;
712

713
714
715
716
717
718
719
    /* 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;
    }
720
721
722
723
724
725
726
    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;
    }
727

728
    /* Hook used for callback variables */
729
    p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
730
731
    if( !p_input )
    {
732
        free( p_sys );
733
        free( p_logo_list );
734
735
        return VLC_ENOOBJ;
    }
736

737
    p_logo_list->psz_filename =
dionoea's avatar
dionoea committed
738
        var_CreateGetString( p_input->p_libvlc , "logo-file" );
739
    if( !p_logo_list->psz_filename || !*p_logo_list->psz_filename )
740
741
    {
        msg_Err( p_this, "logo file not specified" );
742
        vlc_object_release( p_input );
743
        //if( p_sys->psz_filename ) free( p_sys->psz_filename );
744
        free( p_sys );
745
        free( p_logo_list );
746
        return VLC_EGENERIC;
747
748
    }

749
750
751
    p_sys->posx = var_CreateGetInteger( p_input->p_libvlc , "logo-x" );
    p_sys->posy = var_CreateGetInteger( p_input->p_libvlc , "logo-y" );
    p_sys->pos = var_CreateGetInteger( p_input->p_libvlc , "logo-position" );
752
753
754
755
756
757
    p_logo_list->i_alpha = __MAX( __MIN( var_CreateGetInteger(
                           p_input->p_libvlc, "logo-transparency"), 255 ), 0 );
    p_logo_list->i_delay =
                    var_CreateGetInteger( p_input->p_libvlc , "logo-delay" );
    p_logo_list->i_repeat =
                    var_CreateGetInteger( p_input->p_libvlc , "logo-repeat" );
758

759
760
761
762
763
    var_AddCallback( p_input->p_libvlc, "logo-file", LogoCallback, p_sys );
    var_AddCallback( p_input->p_libvlc, "logo-x", LogoCallback, p_sys );
    var_AddCallback( p_input->p_libvlc, "logo-y", LogoCallback, p_sys );
    var_AddCallback( p_input->p_libvlc, "logo-position", LogoCallback, p_sys );
    var_AddCallback( p_input->p_libvlc, "logo-transparency", LogoCallback, p_sys );
764
    var_AddCallback( p_input->p_libvlc, "logo-repeat", LogoCallback, p_sys );
765
    vlc_object_release( p_input );
766

767
768
769
770
771
772
    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 );
773
774
775

    /* Misc init */
    p_filter->pf_sub_filter = Filter;
776
    p_sys->b_need_update = VLC_TRUE;
777

778
    p_sys->i_last_date = 0;
779
780
781
782
783
784
785
786
787
788
789

    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;
790
    vlc_object_t *p_input;
791

792
793
794
    vlc_mutex_destroy( &p_sys->p_logo_list->lock );
    FreeLogoList( p_sys->p_logo_list );
    free( p_sys->p_logo_list );
795
    free( p_sys );
796

797
    /* Delete the logo variables from INPUT */
798
799
800
    p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
    if( !p_input ) return;

801
802
803
    var_Destroy( p_input->p_libvlc , "logo-file" );
    var_Destroy( p_input->p_libvlc , "logo-x" );
    var_Destroy( p_input->p_libvlc , "logo-y" );
804
805
    var_Destroy( p_input->p_libvlc , "logo-delay" );
    var_Destroy( p_input->p_libvlc , "logo-repeat" );
806
807
    var_Destroy( p_input->p_libvlc , "logo-position" );
    var_Destroy( p_input->p_libvlc , "logo-transparency" );
808
    vlc_object_release( p_input );
809
810
}

811
/*****************************************************************************
812
 * Filter: the whole thing
813
 *****************************************************************************
814
 * This function outputs subpictures at regular time intervals.
815
 *****************************************************************************/
816
817
818
static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
{
    filter_sys_t *p_sys = p_filter->p_sys;
819
    logo_list_t *p_logo_list = p_sys->p_logo_list;
820
821
822
    subpicture_t *p_spu;
    subpicture_region_t *p_region;
    video_format_t fmt;
823
824
    picture_t *p_pic;
    logo_t *p_logo;
825

826
827
828
829
    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 */
830

831
832
    if( ( ( !p_sys->b_need_update ) && ( p_logo_list->i_next_pic > date ) )
        || !p_logo_list->i_repeat )
833
    {
834
835
        vlc_mutex_unlock( &p_logo_list->lock );
        return 0;
836
    }
837
    /* prior code tested on && p_sys->i_last_date +5000000 > date ) return 0; */
838

839
840
841
842
843
844
    /* 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;
845

846
847
    /* Allocate the subpicture internal data. */
    p_spu = p_filter->pf_sub_buffer_new( p_filter );
848
849
850
851
852
    if( !p_spu )
    {
        vlc_mutex_unlock( &p_logo_list->lock );
        return NULL;
    }
853

854
855
856
857
858
859
    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;
860
861
862
863
864
865
866
867
868
869
870
871
872
    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;
        }
    }
873

874
875
    if( !p_pic || !p_logo->i_alpha
        || ( p_logo->i_alpha == -1 && !p_logo_list->i_alpha ) )
876
877
    {
        /* Send an empty subpicture to clear the display */
878
        vlc_mutex_unlock( &p_logo_list->lock );
879
880
881
        return p_spu;
    }

882
883
884
885
    /* 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;
886
    fmt.i_sar_num = fmt.i_sar_den = 1;
887
888
    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;
889
890
891
892
893
894
    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 );
895
        vlc_mutex_unlock( &p_logo_list->lock );
896
897
        return NULL;
    }
898

899
900
    vout_CopyPicture( p_filter, &p_region->picture, p_pic );
    vlc_mutex_unlock( &p_logo_list->lock );
901
902
903
904
905
906
907
908
909
910
911

    /*  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
912
        p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
913
914
915
916
917
        p_spu->i_x = p_sys->posx;
        p_spu->i_y = p_sys->posy;
        p_spu->b_absolute = VLC_TRUE;
    }

918
    p_spu->p_region = p_region;
919
920
921

    p_spu->i_alpha = ( p_logo->i_alpha != -1 ?
                       p_logo->i_alpha : p_logo_list->i_alpha );
922
923
924

    return p_spu;
}
925
926

/*****************************************************************************
927
 * Callback to update params on the fly
928
 *****************************************************************************/
929
static int LogoCallback( vlc_object_t *p_this, char const *psz_var,
930
                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
931
{
932
    filter_sys_t *p_sys = (filter_sys_t *)p_data;
933
    logo_list_t *p_logo_list = p_sys->p_logo_list;
934

935
    if( !strncmp( psz_var, "logo-file", 6 ) )
936
    {
937
938
939
940
941
942
        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;
943
    }
944
    else if ( !strncmp( psz_var, "logo-x", 6 ) )
945
946
947
948
949
950
951
952
953
954
955
    {
        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;
    }
956
957
    else if ( !strncmp( psz_var, "logo-transparency", 9 ) )
    {
958
959
960
961
962
963
964
965
966
        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 );
967
    }
968
969
970
    p_sys->b_need_update = VLC_TRUE;
    return VLC_SUCCESS;
}