logo.c 33.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
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_common.h>
34
#include <vlc_plugin.h>
Clément Stenac's avatar
Clément Stenac committed
35
#include <vlc_vout.h>
36

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

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

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

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

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

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

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

96 97
#define CFG_PREFIX "logo-"

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

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

112 113 114
    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 );
115
    /* default to 1000 ms per image, continuously cycle through them */
116 117
    add_integer( CFG_PREFIX "delay", 1000, NULL, DELAY_TEXT, DELAY_LONGTEXT, true );
    add_integer( CFG_PREFIX "repeat", -1, NULL, REPEAT_TEXT, REPEAT_LONGTEXT, true );
118
    add_integer_with_range( CFG_PREFIX "transparency", 255, 0, 255, NULL,
119 120
        TRANS_TEXT, TRANS_LONGTEXT, false );
    add_integer( CFG_PREFIX "position", -1, NULL, POS_TEXT, POS_LONGTEXT, false );
121
        change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
122

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

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

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

169
/*****************************************************************************
170
 * LoadImage: loads the logo image into memory
171
 *****************************************************************************/
172
static picture_t *LoadImage( vlc_object_t *p_this, char *psz_filename )
173
{
174
    picture_t *p_pic;
175
    image_handler_t *p_image;
176 177 178 179 180
    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) );
181

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

    return p_pic;
188 189
}

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

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

298 299 300 301 302
    vout_thread_t *p_vout;

    filter_t *p_blend;

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

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

    /* Allocate structure */
316 317
    p_sys = p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
    if( p_sys == NULL )
318
        return VLC_ENOMEM;
319 320 321 322 323 324
    p_logo_list = p_sys->p_logo_list = malloc( sizeof( logo_list_t ) );
    if( p_logo_list == NULL )
    {
        free( p_sys );
        return VLC_ENOMEM;
    }
325

326 327 328 329
    config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options,
                       p_vout->p_cfg );

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

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

344 345 346
    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" );
347
    p_logo_list->i_delay = __MAX( __MIN(
348 349
        var_CreateGetIntegerCommand( p_vout, "logo-delay" ) , 60000 ), 0 );
    p_logo_list->i_repeat = var_CreateGetIntegerCommand( p_vout, "logo-repeat");
350
    p_logo_list->i_alpha = __MAX( __MIN(
351
        var_CreateGetIntegerCommand( p_vout, "logo-transparency" ), 255 ), 0 );
352

353
    LoadLogoList( p_vout, p_logo_list );
354

355 356 357 358 359 360 361 362
    return VLC_SUCCESS;
}

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

369
    I_OUTPUTPICTURES = 0;
370
    memset( &fmt, 0, sizeof(video_format_t) );
371

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

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

421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
    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
443 444 445 446
    else
    {
        p_sys->pos = 0;
    }
447

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

451
    p_sys->p_vout = vout_Create( p_vout, &fmt );
452 453

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

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

    ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
464
    ADD_CALLBACKS( p_sys->p_vout, SendEvents );
Gildas Bazin's avatar
 
Gildas Bazin committed
465 466
    ADD_PARENT_CALLBACKS( SendEventsToChild );

467 468 469 470 471 472 473 474
    return VLC_SUCCESS;
}

/*****************************************************************************
 * End: terminate logo video thread output method
 *****************************************************************************/
static void End( vout_thread_t *p_vout )
{
475
    vout_sys_t *p_sys = p_vout->p_sys;
476 477
    int i_index;

478 479 480 481
    DEL_PARENT_CALLBACKS( SendEventsToChild );

    DEL_CALLBACKS( p_sys->p_vout, SendEvents );

482 483 484 485 486 487 488
    /* Free the fake output buffers we allocated */
    for( i_index = I_OUTPUTPICTURES ; i_index ; )
    {
        i_index--;
        free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
    }

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

492
    vout_CloseAndRelease( p_sys->p_vout );
493

494 495 496
    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 );
497
    vlc_object_release( p_sys->p_blend );
498 499 500 501 502 503 504 505
}

/*****************************************************************************
 * 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;
506
    vout_sys_t *p_sys = p_vout->p_sys;
507

Gildas Bazin's avatar
 
Gildas Bazin committed
508

509 510 511
    FreeLogoList( p_sys->p_logo_list );
    free( p_sys->p_logo_list );

512
    free( p_sys );
513 514 515
}

/*****************************************************************************
516
 * Render: render the logo onto the video
517
 *****************************************************************************/
518
static void Render( vout_thread_t *p_vout, picture_t *p_inpic )
519
{
520
    vout_sys_t *p_sys = p_vout->p_sys;
521
    picture_t *p_outpic;
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
    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
549
            if( p_sys->pos )
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
            {
                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;
    }
576 577

    /* This is a new frame. Get a structure from the video_output. */
578
    while( !(p_outpic = vout_CreatePicture( p_sys->p_vout, 0, 0, 0 )) )
579
    {
580
        if( !vlc_object_alive (p_vout) || p_vout->b_error ) return;
581 582 583
        msleep( VOUT_OUTMEM_SLEEP );
    }

584 585
    vout_CopyPicture( p_vout, p_outpic, p_inpic );
    vout_DatePicture( p_sys->p_vout, p_outpic, p_inpic->date );
586

587
    if( p_pic )
588
    p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_outpic, p_outpic,
589 590 591
                                    p_pic, p_sys->posx, p_sys->posy,
                                    p_logo->i_alpha != -1 ? p_logo->i_alpha
                                    : p_logo_list->i_alpha );
592

593
    vout_DisplayPicture( p_sys->p_vout, p_outpic );
594 595 596 597 598 599 600 601
}

/*****************************************************************************
 * 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
602
    VLC_UNUSED(p_this); VLC_UNUSED(oldval);
603 604 605 606 607 608
    var_Set( (vlc_object_t *)p_data, psz_var, newval );
    return VLC_SUCCESS;
}

/*****************************************************************************
 * MouseEvent: callback for mouse events
609
 *****************************************************************************/
610 611 612
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
613
    VLC_UNUSED(p_this); VLC_UNUSED(oldval);
614
    vout_thread_t *p_vout = (vout_thread_t*)p_data;
615
    vout_sys_t *p_sys = p_vout->p_sys;
616 617 618 619 620 621 622
    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;

623
    if( (valb.i_int & 0x1) == 0 )
624 625 626 627 628 629 630 631
    {
        return VLC_SUCCESS;
    }

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

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

658 659 660 661 662 663 664 665
/*****************************************************************************
 * 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
666 667 668 669 670 671
/*****************************************************************************
 * 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
672
    VLC_UNUSED(p_data); VLC_UNUSED(oldval);
Gildas Bazin's avatar
 
Gildas Bazin committed
673 674 675 676
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
    var_Set( p_vout->p_sys->p_vout, psz_var, newval );
    return VLC_SUCCESS;
}
677 678 679 680 681 682

/*****************************************************************************
 * filter_sys_t: logo filter descriptor
 *****************************************************************************/
struct filter_sys_t
{
683
    logo_list_t *p_logo_list;
684

685
    int pos, posx, posy;
686

687
    bool b_absolute;
688
    mtime_t i_last_date;
689 690

    /* On the fly control variable */
691
    bool b_need_update;
692 693 694 695 696 697 698 699 700 701 702
};

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;
703
    logo_list_t *p_logo_list;
704

705 706 707 708
    /* Allocate structure */
    p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
    if( p_sys == NULL )
        return VLC_ENOMEM;
709 710 711 712 713 714
    p_logo_list = p_sys->p_logo_list = malloc( sizeof( logo_list_t ) );
    if( p_logo_list == NULL )
    {
        free( p_sys );
        return VLC_ENOMEM;
    }
715

716 717 718
    config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
                       p_filter->p_cfg );

719
    /* Hook used for callback variables */
720
    p_logo_list->psz_filename =
721
        var_CreateGetStringCommand( p_filter, "logo-file" );
722
    if( !p_logo_list->psz_filename || !*p_logo_list->psz_filename )
723 724
    {
        msg_Err( p_this, "logo file not specified" );
725
        free( p_sys );
726
        free( p_logo_list );
727
        return VLC_EGENERIC;
728 729
    }

730 731 732 733 734
    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 );
735
    p_logo_list->i_delay =
736
        var_CreateGetIntegerCommand( p_filter, "logo-delay" );
737
    p_logo_list->i_repeat =
738
        var_CreateGetIntegerCommand( p_filter, "logo-repeat" );
739

740 741 742 743 744 745
    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 );
746

747
    vlc_mutex_init( &p_logo_list->lock );
748 749 750 751 752
    vlc_mutex_lock( &p_logo_list->lock );

    LoadLogoList( p_this, p_logo_list );

    vlc_mutex_unlock( &p_logo_list->lock );
753 754 755

    /* Misc init */
    p_filter->pf_sub_filter = Filter;
756
    p_sys->b_need_update = true;
757

758
    p_sys->i_last_date = 0;
759 760 761 762 763 764 765 766 767 768 769 770

    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;

771 772 773
    vlc_mutex_destroy( &p_sys->p_logo_list->lock );
    FreeLogoList( p_sys->p_logo_list );
    free( p_sys->p_logo_list );
774
    free( p_sys );
775

776
    /* Delete the logo variables from INPUT */
777 778 779 780 781 782 783
    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" );
784 785
}

786
/*****************************************************************************
787
 * Filter: the whole thing
788
 *****************************************************************************
789
 * This function outputs subpictures at regular time intervals.
790
 *****************************************************************************/
791 792 793
static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
{
    filter_sys_t *p_sys = p_filter->p_sys;
794
    logo_list_t *p_logo_list = p_sys->p_logo_list;
795 796 797
    subpicture_t *p_spu;
    subpicture_region_t *p_region;
    video_format_t fmt;
798 799
    picture_t *p_pic;
    logo_t *p_logo;
800

801 802 803 804
    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 */
805

806 807
    if( ( ( !p_sys->b_need_update ) && ( p_logo_list->i_next_pic > date ) )
        || !p_logo_list->i_repeat )
808
    {
809 810
        vlc_mutex_unlock( &p_logo_list->lock );
        return 0;
811
    }
812
    /* prior code tested on && p_sys->i_last_date +5000000 > date ) return 0; */
813

814 815 816 817 818 819
    /* 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;
820

821 822
    /* Allocate the subpicture internal data. */
    p_spu = p_filter->pf_sub_buffer_new( p_filter );
823 824 825 826 827
    if( !p_spu )
    {
        vlc_mutex_unlock( &p_logo_list->lock );
        return NULL;
    }
828

829 830 831
    p_spu->b_absolute = p_sys->b_absolute;
    p_spu->i_start = p_sys->i_last_date = date;
    p_spu->i_stop = 0;
832
    p_spu->b_ephemer = true;
833

834
    p_sys->b_need_update = false;
835 836 837 838 839 840 841 842 843 844 845 846 847
    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;
        }
    }
848

849 850
    if( !p_pic || !p_logo->i_alpha
        || ( p_logo->i_alpha == -1 && !p_logo_list->i_alpha ) )
851 852
    {
        /* Send an empty subpicture to clear the display */
853
        vlc_mutex_unlock( &p_logo_list->lock );
854 855 856
        return p_spu;
    }

857 858 859 860
    /* 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;
861
    fmt.i_sar_num = fmt.i_sar_den = 1;
862 863
    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;
864 865 866 867 868 869
    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 );
870
        vlc_mutex_unlock( &p_logo_list->lock );
871 872
        return NULL;
    }
873

874 875
    vout_CopyPicture( p_filter, &p_region->picture, p_pic );
    vlc_mutex_unlock( &p_logo_list->lock );
876 877

    /*  where to locate the logo: */
878 879
    if( p_sys->pos < 0 )
    {   /*  set to an absolute xy */
880
        p_region->i_align = OSD_ALIGN_RIGHT | OSD_ALIGN_TOP;
881
        p_spu->b_absolute = true;
882 883
    }
    else
884
    {   /* set to one of the 9 relative locations */
885
        p_region->i_align = p_sys->pos;
886
        p_spu->b_absolute = false;
887 888
    }

889 890 891
    p_spu->i_x = p_sys->posx;
    p_spu->i_y = p_sys->posy;

892
    p_spu->p_region = p_region;
893 894 895

    p_spu->i_alpha = ( p_logo->i_alpha != -1 ?
                       p_logo->i_alpha : p_logo_list->i_alpha );
896 897 898

    return p_spu;
}
899 900

/*****************************************************************************
901
 * Callback to update params on the fly
902
 *****************************************************************************/
903
static int LogoCallback( vlc_object_t *p_this, char const *psz_var,
904
                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
905
{
Rafaël Carré's avatar
Rafaël Carré committed
906
    VLC_UNUSED(oldval);
907
    filter_sys_t *p_sys = (filter_sys_t *)p_data;
908
    logo_list_t *p_logo_list = p_sys->p_logo_list;
909

910
    if( !strncmp( psz_var, "logo-file", 6 ) )
911
    {
912 913 914 915 916
        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 );
917
        p_sys->b_need_update = true;
918
    }
919
    else if ( !strncmp( psz_var, "logo-x", 6 ) )
920 921 922 923 924 925 926 927 928 929 930
    {
        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;
    }
931 932
    else if ( !strncmp( psz_var, "logo-transparency", 9 ) )
    {
933 934 935 936 937 938 939 940 941
        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 );
942
    }
943
    p_sys->b_need_update = true;
944 945
    return VLC_SUCCESS;
}