logo.c 33.5 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
Antoine Cellerier's avatar
Antoine Cellerier committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24 25 26 27 28
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

29 30 31 32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33
#include <vlc/vlc.h>
Clément Stenac's avatar
Clément Stenac committed
34
#include <vlc_vout.h>
35

36
#include "vlc_filter.h"
37
#include "filter_common.h"
38
#include "vlc_image.h"
39
#include "vlc_osd.h"
40

Gildas Bazin's avatar
Gildas Bazin committed
41 42 43 44
#ifdef LoadImage
#   undef LoadImage
#endif

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

61 62 63
static int  CreateFilter ( vlc_object_t * );
static void DestroyFilter( vlc_object_t * );

64 65
static int LogoCallback( vlc_object_t *, char const *,
                         vlc_value_t, vlc_value_t, void * );
66

67 68 69
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
70
#define FILE_TEXT N_("Logo filenames")
71 72 73
#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.")
74
#define REPEAT_TEXT N_("Logo animation # of loops")
75
#define REPEAT_LONGTEXT N_("Number of loops for the logo animation." \
Christophe Mutricy's avatar
typo  
Christophe Mutricy committed
76
        "-1 = continuous, 0 = disabled")
77
#define DELAY_TEXT N_("Logo individual image time in ms")
78
#define DELAY_LONGTEXT N_("Individual image display time of 0 - 60000 ms.")
79

80 81 82 83 84 85
#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." )
86
#define TRANS_TEXT N_("Transparency of the logo")
87
#define TRANS_LONGTEXT N_("Logo transparency value " \
88
  "(from 0 for full transparency to 255 for full opacity)." )
89 90
#define POS_TEXT N_("Logo position")
#define POS_LONGTEXT N_( \
91
  "Enforce the logo position on the video " \
92
  "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
93
  "also use combinations of these values, eg 6 = top-right).")
94

95 96
#define CFG_PREFIX "logo-"

97
static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
98
static const char *ppsz_pos_descriptions[] =
99 100
{ N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
  N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
101 102

vlc_module_begin();
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
103 104
    set_description( _("Logo video filter") );
    set_capability( "video filter", 0 );
105
    set_shortname( _("Logo overlay") );
Clément Stenac's avatar
Clément Stenac committed
106
    set_category( CAT_VIDEO );
107
    set_subcategory( SUBCAT_VIDEO_SUBPIC );
108 109
    add_shortcut( "logo" );
    set_callbacks( Create, Destroy );
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
110

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

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

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

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 167
/*****************************************************************************
 * 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;

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

    memset( &fmt_in, 0, sizeof(video_format_t) );
    memset( &fmt_out, 0, sizeof(video_format_t) );
180

181 182 183 184
    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 );
185 186

    return p_pic;
187 188
}

189 190 191 192 193 194 195 196 197
/*****************************************************************************
 * 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 )
198
static void __LoadLogoList( vlc_object_t *p_this, logo_list_t *p_logo_list )
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
{
    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 )
        {
251
            msg_Warn( p_this, "error while loading logo %s, will be skipped",
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
                      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
 *****************************************************************************/
271
static void FreeLogoList( logo_list_t *p_logo_list )
272 273
{
    unsigned int i;
274
    FREENULL( p_logo_list->psz_filename );
275 276 277
    for( i = 0; i < p_logo_list->i_count; i++ )
    {
        logo_t *p_logo = &p_logo_list->p_logo[i];
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
278 279
        FREENULL( p_logo->psz_file );
        if( p_logo->p_pic )
280
        {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
281 282
            p_logo->p_pic->pf_release( p_logo->p_pic );
            p_logo->p_pic = NULL;
283 284 285 286
        }
    }
}

287
/*****************************************************************************
288
 * vout_sys_t: logo video output method descriptor
289
 *****************************************************************************
290 291 292 293 294
 * This structure is part of the video output thread descriptor.
 * It describes the Invert specific properties of an output thread.
 *****************************************************************************/
struct vout_sys_t
{
295 296
    logo_list_t *p_logo_list;

297 298 299 300 301
    vout_thread_t *p_vout;

    filter_t *p_blend;

    int i_width, i_height;
302
    int pos, posx, posy;
303 304 305 306
};

/*****************************************************************************
 * Create: allocates logo video thread output method
307 308 309 310
 *****************************************************************************/
static int Create( vlc_object_t *p_this )
{
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
311
    vout_sys_t *p_sys;
312
    logo_list_t *p_logo_list;
313 314

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

329 330 331 332
    config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options,
                       p_vout->p_cfg );

    p_logo_list->psz_filename = var_CreateGetStringCommand( p_vout,
333
                                                            "logo-file" );
334
    if( !p_logo_list->psz_filename || !*p_logo_list->psz_filename )
335
    {
336
        msg_Err( p_vout, "logo file not specified" );
337
        return VLC_EGENERIC;
338
    }
339

340 341 342 343 344 345 346
    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;
    p_vout->pf_control = Control;

347 348 349
    p_sys->pos = var_CreateGetIntegerCommand( p_vout, "logo-position" );
    p_sys->posx = var_CreateGetIntegerCommand( p_vout, "logo-x" );
    p_sys->posy = var_CreateGetIntegerCommand( p_vout, "logo-y" );
350
    p_logo_list->i_delay = __MAX( __MIN(
351 352
        var_CreateGetIntegerCommand( p_vout, "logo-delay" ) , 60000 ), 0 );
    p_logo_list->i_repeat = var_CreateGetIntegerCommand( p_vout, "logo-repeat");
353
    p_logo_list->i_alpha = __MAX( __MIN(
354
        var_CreateGetIntegerCommand( p_vout, "logo-transparency" ), 255 ), 0 );
355

356
    LoadLogoList( p_vout, p_logo_list );
357

358 359 360 361 362 363 364 365
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Init: initialize logo video thread output method
 *****************************************************************************/
static int Init( vout_thread_t *p_vout )
{
366
    vout_sys_t *p_sys = p_vout->p_sys;
367
    picture_t *p_pic;
368
    int i_index;
369
    video_format_t fmt;
370 371
    logo_list_t *p_logo_list = p_sys->p_logo_list;

372
    I_OUTPUTPICTURES = 0;
373
    memset( &fmt, 0, sizeof(video_format_t) );
374

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

388 389 390 391 392 393 394 395 396 397 398
    /* 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;
399 400 401 402 403 404 405 406
    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;
407 408 409 410 411 412 413 414 415 416 417 418 419
    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 );
420
        vlc_object_release( p_sys->p_blend );
421 422 423
        return VLC_EGENERIC;
    }

424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
    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;
        }
    }
Antoine Cellerier's avatar
Antoine Cellerier committed
446 447 448 449
    else
    {
        p_sys->pos = 0;
    }
450

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

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

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

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

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

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

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

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

491
    if( p_sys->p_vout )
492
    {
493 494 495
        DEL_CALLBACKS( p_sys->p_vout, SendEvents );
        vlc_object_detach( p_sys->p_vout );
        vout_Destroy( p_sys->p_vout );
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 );
501
    vlc_object_release( p_sys->p_blend );
502 503 504 505 506 507 508 509
}

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

Gildas Bazin's avatar
 
Gildas Bazin committed
512 513
    DEL_PARENT_CALLBACKS( SendEventsToChild );

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

517
    free( p_sys );
518 519 520
}

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

Antoine Cellerier's avatar
Antoine Cellerier committed
554
            if( p_sys->pos )
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
            {
                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;
    }
581 582

    /* This is a new frame. Get a structure from the video_output. */
583
    while( !(p_outpic = vout_CreatePicture( p_sys->p_vout, 0, 0, 0 )) )
584
    {
585
        if( p_vout->b_die || p_vout->b_error ) return;
586 587 588
        msleep( VOUT_OUTMEM_SLEEP );
    }

589 590
    vout_CopyPicture( p_vout, p_outpic, p_inpic );
    vout_DatePicture( p_sys->p_vout, p_outpic, p_inpic->date );
591

592
    if( p_pic )
593
    p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_outpic, p_outpic,
594 595 596
                                    p_pic, p_sys->posx, p_sys->posy,
                                    p_logo->i_alpha != -1 ? p_logo->i_alpha
                                    : p_logo_list->i_alpha );
597

598
    vout_DisplayPicture( p_sys->p_vout, p_outpic );
599 600 601 602 603 604 605 606
}

/*****************************************************************************
 * 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 )
{
Rafaël Carré's avatar
Rafaël Carré committed
607
    VLC_UNUSED(p_this); VLC_UNUSED(oldval);
608 609 610 611 612 613
    var_Set( (vlc_object_t *)p_data, psz_var, newval );
    return VLC_SUCCESS;
}

/*****************************************************************************
 * MouseEvent: callback for mouse events
614
 *****************************************************************************/
615 616 617
static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
                       vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
Rafaël Carré's avatar
Rafaël Carré committed
618
    VLC_UNUSED(p_this); VLC_UNUSED(oldval);
619
    vout_thread_t *p_vout = (vout_thread_t*)p_data;
620
    vout_sys_t *p_sys = p_vout->p_sys;
621 622 623 624 625 626 627
    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;

628
    if( (valb.i_int & 0x1) == 0 )
629 630 631 632 633 634 635 636
    {
        return VLC_SUCCESS;
    }

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

    return VLC_SUCCESS;
}
Gildas Bazin's avatar
 
Gildas Bazin committed
662

663 664 665 666 667 668 669 670
/*****************************************************************************
 * 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 );
}

Gildas Bazin's avatar
 
Gildas Bazin committed
671 672 673 674 675 676
/*****************************************************************************
 * 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 )
{
Rafaël Carré's avatar
Rafaël Carré committed
677
    VLC_UNUSED(p_data); VLC_UNUSED(oldval);
Gildas Bazin's avatar
 
Gildas Bazin committed
678 679 680 681
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
    var_Set( p_vout->p_sys->p_vout, psz_var, newval );
    return VLC_SUCCESS;
}
682 683 684 685 686 687

/*****************************************************************************
 * filter_sys_t: logo filter descriptor
 *****************************************************************************/
struct filter_sys_t
{
688
    logo_list_t *p_logo_list;
689

690
    int pos, posx, posy;
691

692
    bool b_absolute;
693
    mtime_t i_last_date;
694 695

    /* On the fly control variable */
696
    bool b_need_update;
697 698 699 700 701 702 703 704 705 706 707
};

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;
708
    logo_list_t *p_logo_list;
709

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

725 726 727
    config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
                       p_filter->p_cfg );

728
    /* Hook used for callback variables */
729
    p_logo_list->psz_filename =
730
        var_CreateGetStringCommand( p_filter, "logo-file" );
731
    if( !p_logo_list->psz_filename || !*p_logo_list->psz_filename )
732 733
    {
        msg_Err( p_this, "logo file not specified" );
734
        free( p_sys );
735
        free( p_logo_list );
736
        return VLC_EGENERIC;
737 738
    }

739 740 741 742 743
    p_sys->posx = var_CreateGetIntegerCommand( p_filter, "logo-x" );
    p_sys->posy = var_CreateGetIntegerCommand( p_filter, "logo-y" );
    p_sys->pos = var_CreateGetIntegerCommand( p_filter, "logo-position" );
    p_logo_list->i_alpha = __MAX( __MIN( var_CreateGetIntegerCommand(
                           p_filter, "logo-transparency"), 255 ), 0 );
744
    p_logo_list->i_delay =
745
        var_CreateGetIntegerCommand( p_filter, "logo-delay" );
746
    p_logo_list->i_repeat =
747
        var_CreateGetIntegerCommand( p_filter, "logo-repeat" );
748

749 750 751 752 753 754
    var_AddCallback( p_filter, "logo-file", LogoCallback, p_sys );
    var_AddCallback( p_filter, "logo-x", LogoCallback, p_sys );
    var_AddCallback( p_filter, "logo-y", LogoCallback, p_sys );
    var_AddCallback( p_filter, "logo-position", LogoCallback, p_sys );
    var_AddCallback( p_filter, "logo-transparency", LogoCallback, p_sys );
    var_AddCallback( p_filter, "logo-repeat", LogoCallback, p_sys );
755

756
    vlc_mutex_init( &p_logo_list->lock );
757 758 759 760 761
    vlc_mutex_lock( &p_logo_list->lock );

    LoadLogoList( p_this, p_logo_list );

    vlc_mutex_unlock( &p_logo_list->lock );
762 763 764

    /* Misc init */
    p_filter->pf_sub_filter = Filter;
765
    p_sys->b_need_update = true;
766

767
    p_sys->i_last_date = 0;
768 769 770 771 772 773 774 775 776 777 778 779

    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;

780 781 782
    vlc_mutex_destroy( &p_sys->p_logo_list->lock );
    FreeLogoList( p_sys->p_logo_list );
    free( p_sys->p_logo_list );
783
    free( p_sys );
784

785
    /* Delete the logo variables from INPUT */
786 787 788 789 790 791 792
    var_Destroy( p_filter->p_libvlc, "logo-file" );
    var_Destroy( p_filter->p_libvlc, "logo-x" );
    var_Destroy( p_filter->p_libvlc, "logo-y" );
    var_Destroy( p_filter->p_libvlc, "logo-delay" );
    var_Destroy( p_filter->p_libvlc, "logo-repeat" );
    var_Destroy( p_filter->p_libvlc, "logo-position" );
    var_Destroy( p_filter->p_libvlc, "logo-transparency" );
793 794
}

795
/*****************************************************************************
796
 * Filter: the whole thing
797
 *****************************************************************************
798
 * This function outputs subpictures at regular time intervals.
799
 *****************************************************************************/
800 801 802
static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
{
    filter_sys_t *p_sys = p_filter->p_sys;
803
    logo_list_t *p_logo_list = p_sys->p_logo_list;
804 805 806
    subpicture_t *p_spu;
    subpicture_region_t *p_region;
    video_format_t fmt;
807 808
    picture_t *p_pic;
    logo_t *p_logo;
809

810 811 812 813
    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 */
814

815 816
    if( ( ( !p_sys->b_need_update ) && ( p_logo_list->i_next_pic > date ) )
        || !p_logo_list->i_repeat )
817
    {
818 819
        vlc_mutex_unlock( &p_logo_list->lock );
        return 0;
820
    }
821
    /* prior code tested on && p_sys->i_last_date +5000000 > date ) return 0; */
822

823 824 825 826 827 828
    /* 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;
829

830 831
    /* Allocate the subpicture internal data. */
    p_spu = p_filter->pf_sub_buffer_new( p_filter );
832 833 834 835 836
    if( !p_spu )
    {
        vlc_mutex_unlock( &p_logo_list->lock );
        return NULL;
    }
837

838 839 840
    p_spu->b_absolute = p_sys->b_absolute;
    p_spu->i_start = p_sys->i_last_date = date;
    p_spu->i_stop = 0;
841
    p_spu->b_ephemer = true;
842

843
    p_sys->b_need_update = false;
844 845 846 847 848 849 850 851 852 853 854 855 856
    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;
        }
    }
857

858 859
    if( !p_pic || !p_logo->i_alpha
        || ( p_logo->i_alpha == -1 && !p_logo_list->i_alpha ) )
860 861
    {
        /* Send an empty subpicture to clear the display */
862
        vlc_mutex_unlock( &p_logo_list->lock );
863 864 865
        return p_spu;
    }

866 867 868 869
    /* 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;
870
    fmt.i_sar_num = fmt.i_sar_den = 1;
871 872
    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;
873 874 875 876 877 878
    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 );
879
        vlc_mutex_unlock( &p_logo_list->lock );
880 881
        return NULL;
    }
882

883 884
    vout_CopyPicture( p_filter, &p_region->picture, p_pic );
    vlc_mutex_unlock( &p_logo_list->lock );
885 886

    /*  where to locate the logo: */
887 888
    if( p_sys->pos < 0 )
    {   /*  set to an absolute xy */
889
        p_region->i_align = OSD_ALIGN_RIGHT | OSD_ALIGN_TOP;
890
        p_spu->b_absolute = true;
891 892
    }
    else
893
    {   /* set to one of the 9 relative locations */
894
        p_region->i_align = p_sys->pos;
895
        p_spu->b_absolute = false;
896 897
    }

898 899 900
    p_spu->i_x = p_sys->posx;
    p_spu->i_y = p_sys->posy;

901
    p_spu->p_region = p_region;
902 903 904

    p_spu->i_alpha = ( p_logo->i_alpha != -1 ?
                       p_logo->i_alpha : p_logo_list->i_alpha );
905 906 907

    return p_spu;
}
908 909

/*****************************************************************************
910
 * Callback to update params on the fly
911
 *****************************************************************************/
912
static int LogoCallback( vlc_object_t *p_this, char const *psz_var,
913
                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
914
{
Rafaël Carré's avatar
Rafaël Carré committed
915
    VLC_UNUSED(oldval);
916
    filter_sys_t *p_sys = (filter_sys_t *)p_data;
917
    logo_list_t *p_logo_list = p_sys->p_logo_list;
918

919
    if( !strncmp( psz_var, "logo-file", 6 ) )
920
    {
921 922 923 924 925
        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 );
926
        p_sys->b_need_update = true;
927
    }
928
    else if ( !strncmp( psz_var, "logo-x", 6 ) )
929 930 931 932 933 934 935 936 937 938 939
    {
        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;
    }
940 941
    else if ( !strncmp( psz_var, "logo-transparency", 9 ) )
    {
942 943 944 945 946 947 948 949 950
        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 );
951
    }
952
    p_sys->b_need_update = true;
953 954
    return VLC_SUCCESS;
}