osdmenu.c 26.1 KB
Newer Older
1
2
3
/*****************************************************************************
 * osdmenu.c: osd filter module
 *****************************************************************************
Jean-Paul Saman's avatar
Jean-Paul Saman committed
4
 * Copyright (C) 2004-2007 M2X
dionoea's avatar
dionoea committed
5
 * $Id$
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * Authors: Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
 *
 * 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 implid 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
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
23
24
25
26
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
27
28
29
30
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

31
#include <vlc/vlc.h>
32
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
33
#include <vlc_vout.h>
34
35
36
37
38
39
40
41
42
#include <vlc_filter.h>

#include <vlc_osd.h>

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/

/* FIXME: Future extension make the definition file in XML format. */
43
#define OSD_FILE_TEXT N_("Configuration file")
44
#define OSD_FILE_LONGTEXT N_( \
Christophe Mutricy's avatar
Christophe Mutricy committed
45
    "Configuration file for the OSD Menu." )
46
47
#define OSD_PATH_TEXT N_("Path to OSD menu images")
#define OSD_PATH_LONGTEXT N_( \
48
    "Path to the OSD menu images. This will override the path defined in the " \
49
50
    "OSD configuration file." )

51
#define POSX_TEXT N_("X coordinate")
52
53
#define POSX_LONGTEXT N_("You can move the OSD menu by left-clicking on it." )

54
#define POSY_TEXT N_("Y coordinate")
55
56
#define POSY_LONGTEXT N_("You can move the OSD menu by left-clicking on it." )

57
#define POS_TEXT N_("Menu position")
58
59
60
#define POS_LONGTEXT N_( \
  "You can enforce the OSD menu position on the video " \
  "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
61
  "also use combinations of these values, eg. 6 = top-right).")
62

63
#define TIMEOUT_TEXT N_("Menu timeout")
64
#define TIMEOUT_LONGTEXT N_( \
Felix Paul Kühne's avatar
Felix Paul Kühne committed
65
66
67
    "OSD menu pictures get a default timeout of 15 seconds added to their " \
    "remaining time. This will ensure that they are at least the specified " \
    "time visible.")
68

69
#define OSD_UPDATE_TEXT N_("Menu update interval" )
70
#define OSD_UPDATE_LONGTEXT N_( \
71
72
73
74
    "The default is to update the OSD menu picture every 200 ms. Shorten the" \
    " update time for environments that experience transmissions errors. " \
    "Be careful with this option as encoding OSD menu pictures is very " \
    "computing intensive. The range is 0 - 1000 ms." )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
75

Jean-Paul Saman's avatar
Jean-Paul Saman committed
76
77
78
79
80
81
82
#define OSD_ALPHA_TEXT N_("Alpha transparency value (default 255)")
#define OSD_ALPHA_LONGTEXT N_( \
    "The transparency of the OSD menu can be changed by giving a value " \
    "between 0 and 255. A lower value specifies more transparency a higher " \
    "means less transparency. The default is being not transparent " \
    "(value 255) the minimum is fully transparent (value 0)." )

83
84
static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
static const char *const ppsz_pos_descriptions[] =
85
86
87
88
89
90
91
{ N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
  N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };

/* subfilter functions */
static int  CreateFilter ( vlc_object_t * );
static void DestroyFilter( vlc_object_t * );
static subpicture_t *Filter( filter_t *, mtime_t );
92

93
static int OSDMenuUpdateEvent( vlc_object_t *, char const *,
Jean-Paul Saman's avatar
Jean-Paul Saman committed
94
                    vlc_value_t, vlc_value_t, void * );
95
96
static int OSDMenuVisibleEvent( vlc_object_t *, char const *,
                    vlc_value_t, vlc_value_t, void * );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
97
98
static int OSDMenuCallback( vlc_object_t *, char const *,
                            vlc_value_t, vlc_value_t, void * );
99

100
101
102
static int MouseEvent( vlc_object_t *, char const *,
                        vlc_value_t , vlc_value_t , void * );

103
104
#define OSD_CFG "osdmenu-"

105
106
107
108
109
110
#if defined( WIN32 ) || defined( UNDER_CE )
#define OSD_DEFAULT_CFG "osdmenu/default.cfg"
#else
#define OSD_DEFAULT_CFG "share/osdmenu/default.cfg"
#endif

111
#define OSD_UPDATE_MIN     0
112
#define OSD_UPDATE_DEFAULT 300
Jean-Paul Saman's avatar
Jean-Paul Saman committed
113
#define OSD_UPDATE_MAX     1000
114

115
vlc_module_begin();
116
117
    add_integer( OSD_CFG "x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, false );
    add_integer( OSD_CFG "y", -1, NULL, POSY_TEXT, POSY_LONGTEXT, false );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
118
    add_integer( OSD_CFG "position", 8, NULL, POS_TEXT, POS_LONGTEXT,
119
                 false );
120
        change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
121
    add_string( OSD_CFG "file", OSD_DEFAULT_CFG, NULL, OSD_FILE_TEXT,
122
        OSD_FILE_LONGTEXT, false );
123
    add_string( OSD_CFG "file-path", NULL, NULL, OSD_PATH_TEXT,
124
        OSD_PATH_LONGTEXT, false );
125
    add_integer( OSD_CFG "timeout", 15, NULL, TIMEOUT_TEXT,
126
        TIMEOUT_LONGTEXT, false );
127
128
    add_integer_with_range( OSD_CFG "update", OSD_UPDATE_DEFAULT,
        OSD_UPDATE_MIN, OSD_UPDATE_MAX, NULL, OSD_UPDATE_TEXT,
129
        OSD_UPDATE_LONGTEXT, true );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
130
    add_integer_with_range( OSD_CFG "alpha", 255, 0, 255, NULL,
131
        OSD_ALPHA_TEXT, OSD_ALPHA_LONGTEXT, true );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
132

133
    set_capability( "sub filter", 100 );
134
135
    set_description( N_("On Screen Display menu") );
    set_shortname( N_("OSD menu") );
136
    add_shortcut( "osdmenu" );
137

138
139
    set_category( CAT_VIDEO );
    set_subcategory( SUBCAT_VIDEO_SUBPIC );
140

141
142
143
144
145
146
147
148
149
150
151
152
    set_callbacks( CreateFilter, DestroyFilter );
vlc_module_end();

/*****************************************************************************
 * Sub filter code
 *****************************************************************************/

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
struct filter_sys_t
{
153
    int          i_position;    /* relative positioning of SPU images */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
154
155
    int          i_x;           /* absolute positioning of SPU images */
    int          i_y;           /* absolute positioning of SPU images */
156
    mtime_t      i_last_date;   /* last mdate SPU object has been sent to SPU subsytem */
157
    mtime_t      i_timeout;     /* duration SPU object is valid on the video output in seconds */
158

159
160
161
    bool   b_absolute;    /* do we use absolute positioning or relative? */
    bool   b_update;      /* Update OSD Menu by sending SPU objects */
    bool   b_visible;     /* OSD Menu is visible */
162
163
    mtime_t      i_update;      /* Update the OSD menu every n ms */
    mtime_t      i_end_date;    /* End data of display OSD menu */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
164
    int          i_alpha;       /* alpha transparency value */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
165

166
    char        *psz_file;      /* OSD Menu configuration file */
167
    char        *psz_path;      /* Path to OSD Menu pictures */
168
    osd_menu_t  *p_menu;        /* pointer to OSD Menu object */
169
170
171

    /* menu interaction */
    vout_thread_t *p_vout;
172
    bool  b_clicked;
173
174
    uint32_t    i_mouse_x;
    uint32_t    i_mouse_y;
175
176
177
178
179
180
181
182
};

/*****************************************************************************
 * CreateFilter: Create the filter and open the definition file
 *****************************************************************************/
static int CreateFilter ( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t *)p_this;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
183
    filter_sys_t *p_sys = NULL;
184

Jean-Paul Saman's avatar
Jean-Paul Saman committed
185
    p_filter->p_sys = p_sys = (filter_sys_t *) malloc( sizeof(filter_sys_t) );
186
187
188
189
    if( !p_filter->p_sys )
    {
        msg_Err( p_filter, "out of memory" );
        return VLC_ENOMEM;
190
    }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
191
    memset( p_sys, 0, sizeof(filter_sys_t) );
192

193
    /* Populating struct */
194
195
196
197
    p_sys->psz_path = var_CreateGetString( p_this, OSD_CFG "file-path" );
    p_sys->psz_file = var_CreateGetString( p_this, OSD_CFG "file" );
    if( (p_sys->psz_file == NULL) ||
        (*p_sys->psz_file == '\0') )
198
199
200
201
202
    {
        msg_Err( p_filter, "unable to get filename" );
        goto error;
    }

Jean-Paul Saman's avatar
Jean-Paul Saman committed
203
204
    p_sys->i_x = var_CreateGetIntegerCommand( p_this, OSD_CFG "x" );
    p_sys->i_y = var_CreateGetIntegerCommand( p_this, OSD_CFG "y" );
205
    p_sys->i_position = var_CreateGetIntegerCommand( p_this, OSD_CFG "position" );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
206
    p_sys->i_alpha = var_CreateGetIntegerCommand( p_this, OSD_CFG "alpha" );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
207

208
    /* in micro seconds - divide by 2 to match user expectations */
209
210
211
212
    p_sys->i_timeout = var_CreateGetIntegerCommand( p_this, OSD_CFG "timeout" );
    p_sys->i_timeout = (mtime_t)(p_sys->i_timeout * 1000000) >> 2;
    p_sys->i_update  = var_CreateGetIntegerCommand( p_this, OSD_CFG "update" );
    p_sys->i_update = (mtime_t)(p_sys->i_update * 1000); /* in micro seconds */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
213
214
215
216
217

    var_AddCallback( p_filter, OSD_CFG "position", OSDMenuCallback, p_sys );
    var_AddCallback( p_filter, OSD_CFG "timeout", OSDMenuCallback, p_sys );
    var_AddCallback( p_filter, OSD_CFG "update", OSDMenuCallback, p_sys );
    var_AddCallback( p_filter, OSD_CFG "alpha", OSDMenuCallback, p_sys );
218

219
    /* Load the osd menu subsystem */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
220
221
    p_sys->p_menu = osd_MenuCreate( p_this, p_sys->psz_file );
    if( p_sys->p_menu == NULL )
222
        goto error;
223

224
225
    p_sys->p_menu->i_position = p_sys->i_position;

226
    /* Check if menu position was overridden */
227
    p_sys->b_absolute = true;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
228
    if( (p_sys->i_x < 0) || (p_sys->i_y < 0) )
229
    {
230
        p_sys->b_absolute = false;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
231
232
        p_sys->p_menu->i_x = 0;
        p_sys->p_menu->i_y = 0;
233
    }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
234
    else if( (p_sys->i_x >= 0) || (p_sys->i_y >= 0) )
235
    {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
236
237
        p_sys->p_menu->i_x = p_sys->i_x;
        p_sys->p_menu->i_y = p_sys->i_y;
238
    }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
239
240
    else if( (p_sys->p_menu->i_x < 0) ||
             (p_sys->p_menu->i_y < 0) )
241
    {
242
        p_sys->b_absolute = false;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
243
244
        p_sys->p_menu->i_x = 0;
        p_sys->p_menu->i_y = 0;
245
    }
246

247
    /* Set up p_filter */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
248
    p_sys->i_last_date = mdate();
249

250
    /* Keep track of OSD Events */
251
252
253
    p_sys->b_update  = false;
    p_sys->b_visible = false;
    p_sys->b_clicked = false;
254

Jean-Paul Saman's avatar
Jean-Paul Saman committed
255
256
257
258
259
    /* Listen to osd menu core updates/visible settings. */
    var_AddCallback( p_sys->p_menu, "osd-menu-update",
                     OSDMenuUpdateEvent, p_filter );
    var_AddCallback( p_sys->p_menu, "osd-menu-visible",
                     OSDMenuVisibleEvent, p_filter );
260
261
262

    /* Attach subpicture filter callback */
    p_filter->pf_sub_filter = Filter;
263

264
265
266
267
268
269
270
271
272
273
274
    p_sys->p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, FIND_ANYWHERE );
    if( p_sys->p_vout )
    {
        var_AddCallback( p_sys->p_vout, "mouse-x",
                        MouseEvent, p_sys );
        var_AddCallback( p_sys->p_vout, "mouse-y",
                        MouseEvent, p_sys );
        var_AddCallback( p_sys->p_vout, "mouse-clicked",
                        MouseEvent, p_sys );
    }

275
276
    es_format_Init( &p_filter->fmt_out, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
    p_filter->fmt_out.i_priority = 0;
277

278
    return VLC_SUCCESS;
279

280
281
error:
    msg_Err( p_filter, "osdmenu filter discarded" );
282
283
284

    osd_MenuDelete( p_this, p_sys->p_menu );
    p_sys->p_menu = NULL;
ivoire's avatar
ivoire committed
285
286
    free( p_sys->psz_file );
    free( p_sys );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
287
    return VLC_EGENERIC;
288
289
290
291
292
293
294
295
296
297
}

/*****************************************************************************
 * DestroyFilter: Make a clean exit of this plugin
 *****************************************************************************/
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;

Jean-Paul Saman's avatar
Jean-Paul Saman committed
298
299
300
301
302
    var_DelCallback( p_filter, OSD_CFG "position", OSDMenuCallback, p_sys );
    var_DelCallback( p_filter, OSD_CFG "timeout", OSDMenuCallback, p_sys );
    var_DelCallback( p_filter, OSD_CFG "update", OSDMenuCallback, p_sys );
    var_DelCallback( p_filter, OSD_CFG "alpha", OSDMenuCallback, p_sys );

303
304
305
306
307
308
309
    if( p_sys )
    {
        var_DelCallback( p_sys->p_menu, "osd-menu-update",
                         OSDMenuUpdateEvent, p_filter );
        var_DelCallback( p_sys->p_menu, "osd-menu-visible",
                         OSDMenuVisibleEvent, p_filter );
    }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
310

311
    if( p_sys && p_sys->p_vout )
312
313
314
315
316
317
318
319
320
321
322
323
    {
        var_DelCallback( p_sys->p_vout, "mouse-x",
                        MouseEvent, p_sys );
        var_DelCallback( p_sys->p_vout, "mouse-y",
                        MouseEvent, p_sys );
        var_DelCallback( p_sys->p_vout, "mouse-clicked",
                        MouseEvent, p_sys );

        vlc_object_release( p_sys->p_vout );
        p_sys->p_vout = NULL;
    }

324
    var_Destroy( p_this, OSD_CFG "file-path" );
325
326
327
328
329
    var_Destroy( p_this, OSD_CFG "file" );
    var_Destroy( p_this, OSD_CFG "x" );
    var_Destroy( p_this, OSD_CFG "y" );
    var_Destroy( p_this, OSD_CFG "position" );
    var_Destroy( p_this, OSD_CFG "timeout" );
330
    var_Destroy( p_this, OSD_CFG "update" );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
331
    var_Destroy( p_this, OSD_CFG "alpha" );
332

333
334
335
    if( p_sys )
    {
        osd_MenuDelete( p_filter, p_sys->p_menu );
336

337
338
339
        free( p_sys->psz_file );
        free( p_sys );
    }
340
341
342
343
344
345
346
347
}

/*****************************************************************************
 * OSDMenuEvent: callback for OSD Menu events
 *****************************************************************************/
static int OSDMenuVisibleEvent( 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
348
349
    VLC_UNUSED(p_this); VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
    VLC_UNUSED(newval);
350
    filter_t *p_filter = (filter_t *) p_data;
351

352
353
    p_filter->p_sys->b_visible = true;
    p_filter->p_sys->b_update = true;
354
355
356
357
358
359
    return VLC_SUCCESS;
}

static int OSDMenuUpdateEvent( 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
360
361
    VLC_UNUSED(p_this); VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
    VLC_UNUSED(newval);
362
    filter_t *p_filter = (filter_t *) p_data;
363
    filter_sys_t *p_sys = p_filter->p_sys;
364

365
    p_sys->b_update = p_sys->b_visible ? true : false;
366
    p_sys->i_end_date = (mtime_t) 0;
367
368
369
370
371
372
373
    return VLC_SUCCESS;
}

#if 0
/*****************************************************************************
 * create_text_region : compose a text region SPU
 *****************************************************************************/
374
static subpicture_region_t *create_text_region( filter_t *p_filter, subpicture_t *p_spu,
375
376
377
    int i_width, int i_height, const char *psz_text )
{
    subpicture_region_t *p_region;
378
379
    video_format_t       fmt;

380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
    /* Create new SPU region */
    memset( &fmt, 0, sizeof(video_format_t) );
    fmt.i_chroma = VLC_FOURCC( 'T','E','X','T' );
    fmt.i_aspect = VOUT_ASPECT_FACTOR;
    fmt.i_sar_num = fmt.i_sar_den = 1;
    fmt.i_width = fmt.i_visible_width = i_width;
    fmt.i_height = fmt.i_visible_height = i_height;
    fmt.i_x_offset = fmt.i_y_offset = 0;
    p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
    if( !p_region )
    {
        msg_Err( p_filter, "cannot allocate another SPU region" );
        return NULL;
    }
    p_region->psz_text = strdup( psz_text );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
395
    p_region->i_x = 0;
396
    p_region->i_y = 40;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
397
398
#if 0
    msg_Dbg( p_filter, "SPU text region position (%d,%d) (%d,%d) [%s]",
Jean-Paul Saman's avatar
Jean-Paul Saman committed
399
        p_region->i_x, p_region->i_y,
400
401
        p_region->fmt.i_width, p_region->fmt.i_height, p_region->psz_text );
#endif
402
403
    return p_region;
}
404
405
406
#endif

/*****************************************************************************
407
 * create_picture_region : compose a picture region SPU
408
409
410
411
 *****************************************************************************/
static subpicture_region_t *create_picture_region( filter_t *p_filter, subpicture_t *p_spu,
    int i_width, int i_height, picture_t *p_pic )
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
412
    subpicture_region_t *p_region = NULL;
413
    video_format_t       fmt;
414

415
    if( !p_spu ) return NULL;
416

417
    /* Create new SPU region */
418
    memset( &fmt, 0, sizeof(video_format_t) );
419
    fmt.i_chroma = (p_pic == NULL) ? VLC_FOURCC('Y','U','V','P') : VLC_FOURCC('Y','U','V','A');
420
421
422
423
424
    fmt.i_aspect = VOUT_ASPECT_FACTOR;
    fmt.i_sar_num = fmt.i_sar_den = 1;
    fmt.i_width = fmt.i_visible_width = i_width;
    fmt.i_height = fmt.i_visible_height = i_height;
    fmt.i_x_offset = fmt.i_y_offset = 0;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
425

426
    p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
427
428
429
430
431
432
    if( !p_region )
    {
        msg_Err( p_filter, "cannot allocate SPU region" );
        p_filter->pf_sub_buffer_del( p_filter, p_spu );
        return NULL;
    }
433
    if( !p_pic && ( fmt.i_chroma == VLC_FOURCC('Y','U','V','P') ) )
434
    {
435
        p_region->fmt.p_palette->i_entries = 0;
436
437
        p_region->fmt.i_width = p_region->fmt.i_visible_width = 0;
        p_region->fmt.i_height = p_region->fmt.i_visible_height = 0;
438
    }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
439
    if( p_pic )
440
441
        vout_CopyPicture( p_filter, &p_region->picture, p_pic );

442
443
    p_region->i_x = 0;
    p_region->i_y = 0;
444
    p_region->i_align = p_filter->p_sys->i_position;
445
    p_region->i_alpha = p_filter->p_sys->i_alpha;
446
#if 0
447
448
    msg_Dbg( p_filter, "SPU picture region position (%d,%d) (%d,%d) [%p]",
        p_region->i_x, p_region->i_y,
449
450
451
452
453
454
455
456
457
458
459
460
        p_region->fmt.i_width, p_region->fmt.i_height, p_pic );
#endif
    return p_region;
}

/****************************************************************************
 * Filter: the whole thing
 ****************************************************************************
 * This function outputs subpictures at regular time intervals.
 ****************************************************************************/
static subpicture_t *Filter( filter_t *p_filter, mtime_t i_date )
{
461
    filter_sys_t *p_sys = p_filter->p_sys;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
462
463
    subpicture_t *p_spu = NULL;
    subpicture_region_t *p_region = NULL;
464

465
    if( !p_sys->b_update || (p_sys->i_update <= 0) )
466
            return NULL;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
467

Jean-Paul Saman's avatar
Jean-Paul Saman committed
468
469
    /* Am I too early?
    */
470
471
472
    if( ( ( p_sys->i_last_date + p_sys->i_update ) > i_date ) &&
        ( p_sys->i_end_date > 0 ) )
        return NULL; /* we are too early, so wait */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
473

474
475
476
    /* Allocate the subpicture internal data. */
    p_spu = p_filter->pf_sub_buffer_new( p_filter );
    if( !p_spu ) return NULL;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
477

478
479
    p_spu->b_ephemer = true;
    p_spu->b_fade = true;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
480
    if( p_filter->p_sys->p_menu->i_style == OSD_MENU_STYLE_CONCAT )
481
        p_spu->b_absolute = true;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
482
483
    else
        p_spu->b_absolute = p_sys->b_absolute;
484
    p_spu->i_flags = p_sys->i_position;
485

486
487
488
489
490
491
    /* Determine the duration of the subpicture */
    if( p_sys->i_end_date > 0 )
    {
        /* Display the subpicture again. */
        p_spu->i_stop = p_sys->i_end_date - i_date;
        if( ( i_date + p_sys->i_update ) >= p_sys->i_end_date )
492
            p_sys->b_update = false;
493
494
495
496
497
498
499
    }
    else
    {
        /* There is a new OSD picture to display */
        p_spu->i_stop = i_date + p_sys->i_timeout;
        p_sys->i_end_date = p_spu->i_stop;
    }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
500

501
502
503
    p_sys->i_last_date = i_date;
    p_spu->i_start = p_sys->i_last_date = i_date;

504
505
506
    /* Send an empty subpicture to clear the display
     * when OSD menu should be hidden and menu picture is not allocated.
     */
507
    if( !p_filter->p_sys->p_menu->p_state->p_pic ||
508
        ( p_filter->p_sys->b_visible == false ) )
509
    {
510
511
        /* Create new spu regions and allocate an empty picture in it. */
        p_region = create_picture_region( p_filter, p_spu,
512
            p_filter->p_sys->p_menu->p_state->i_width,
513
            p_filter->p_sys->p_menu->p_state->i_height,
514
            NULL );
515

516
517
        /* proper positioning of OSD menu image */
        p_spu->i_x = p_filter->p_sys->p_menu->p_state->i_x;
518
        p_spu->i_y = p_filter->p_sys->p_menu->p_state->i_y;
519
        p_spu->p_region = p_region;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
520
        p_spu->i_alpha = 0xFF; /* Picture is completely non transparent. */
521
        return p_spu;
522
    }
523

524
525
    if( p_sys->p_vout && p_sys->b_clicked )
    {
526
        p_sys->b_clicked = false;
527
528
        osd_MenuActivate( p_filter );
    }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
529
530
    /* Create new spu regions
    */
531
    p_region = create_picture_region( p_filter, p_spu,
532
        p_filter->p_sys->p_menu->p_state->i_width,
533
        p_filter->p_sys->p_menu->p_state->i_height,
534
        p_filter->p_sys->p_menu->p_state->p_pic );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608

    if( !p_region )
    {
        p_filter->pf_sub_buffer_del( p_filter, p_spu );
        return NULL;
    }

    p_spu->i_width = p_region->fmt.i_visible_width;
    p_spu->i_height = p_region->fmt.i_visible_height;
    p_spu->i_alpha = p_filter->p_sys->i_alpha;

    /* proper positioning of OSD menu image */
    if( p_filter->p_sys->p_menu->i_style == OSD_MENU_STYLE_CONCAT )
    {
        p_spu->i_x = p_filter->p_sys->p_menu->p_button->i_x;
        p_spu->i_y = p_filter->p_sys->p_menu->p_button->i_y;
    }
    else
    {
        p_spu->i_x = p_filter->p_sys->p_menu->p_state->i_x;
        p_spu->i_y = p_filter->p_sys->p_menu->p_state->i_y;
    }

    if( p_filter->p_sys->p_menu->i_style == OSD_MENU_STYLE_CONCAT )
    {
        subpicture_region_t *p_region_list = NULL;
        subpicture_region_t *p_region_tail = NULL;
        osd_menu_t *p_osd = p_filter->p_sys->p_menu;
        osd_button_t *p_button = p_osd->p_button;

        /* Construct the entire OSD from individual images */
        while( p_button != NULL )
        {
            osd_button_t *p_tmp = NULL;
            subpicture_region_t *p_new = NULL;

            p_new = create_picture_region( p_filter, p_spu,
                    p_button->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,
                    p_button->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,
                    p_button->p_current_state->p_pic );
            if( !p_new )
            {
                /* Cleanup when bailing out */
                subpicture_region_t *p_tmp = NULL;
                while( p_region_list )
                {
                    p_tmp = p_region_list->p_next;
                    p_spu->pf_destroy_region( VLC_OBJECT(p_filter), p_region_list );
                };
                p_spu->pf_destroy_region( VLC_OBJECT(p_filter), p_region );
                p_filter->pf_sub_buffer_del( p_filter, p_spu );
                return NULL;
            }

            p_spu->i_width += p_new->fmt.i_visible_width;
            p_spu->i_height += p_new->fmt.i_visible_height;

            if( !p_region_list )
            {
                p_region_list = p_new;
                p_region_tail = p_new;
            }
            else
            {
                p_new->i_x = p_region_tail->fmt.i_visible_width;
                p_new->i_y = p_button->i_y;
                p_region_tail->p_next = p_new;
                p_region_tail = p_new;
            }
            p_tmp = p_button->p_next;
            p_button = p_tmp;
        };
        p_region->p_next = p_region_list;
    }
609
#if 0
610
611
    p_region->p_next = create_text_region( p_filter, p_spu,
        p_filter->p_sys->p_menu->p_state->i_width, p_filter->p_sys->p_menu->p_state->i_height,
612
        p_filter->p_sys->p_menu->p_state->p_visible->psz_action );
613
#endif
614
615
616
    p_spu->p_region = p_region;
    return p_spu;
}
Jean-Paul Saman's avatar
Jean-Paul Saman committed
617
618
619
620
621

static int OSDMenuCallback( 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
622
    VLC_UNUSED(p_this); VLC_UNUSED(oldval);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
623
624
625
626
627
628
629
630
631
632
633
634
635
    filter_sys_t *p_sys = (filter_sys_t *) p_data;

    if( !p_sys )
        return VLC_SUCCESS;

    if( !strncmp( psz_var, OSD_CFG"position", 16) )
    {
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
        unsigned int i;
        for( i=0; i < ARRAY_SIZE(pi_pos_values); i++ )
        {
            if( newval.i_int == pi_pos_values[i] )
            {
636
                p_sys->i_position = newval.i_int % 11;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
637
638
639
640
641
642
643
644
                break;
            }
        }
#undef ARRAY_SIZE
    }
    else if( !strncmp( psz_var, OSD_CFG"x", 9) ||
             !strncmp( psz_var, OSD_CFG"y", 9))
    {
645
        p_sys->b_absolute = true;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
646
647
        if( (p_sys->i_x < 0) || (p_sys->i_y < 0) )
        {
648
            p_sys->b_absolute = false;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
649
650
651
652
653
654
655
656
657
658
            p_sys->p_menu->i_x = 0;
            p_sys->p_menu->i_y = 0;
        }
        else if( (p_sys->i_x >= 0) || (p_sys->i_y >= 0) )
        {
            p_sys->p_menu->i_x = p_sys->i_x;
            p_sys->p_menu->i_y = p_sys->i_y;
        }
    }
    else if( !strncmp( psz_var, OSD_CFG"update", 14) )
659
        p_sys->i_update =  (mtime_t)(newval.i_int * 1000);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
660
661
662
663
664
    else if( !strncmp( psz_var, OSD_CFG"timeout", 15) )
        p_sys->i_update = newval.i_int % 1000;
    else if( !strncmp( psz_var, OSD_CFG"alpha", 13) )
        p_sys->i_alpha = newval.i_int % 256;

665
    p_sys->b_update = p_sys->b_visible ? true : false;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
666
667
    return VLC_SUCCESS;
}
668
669
670
671
672
673
674

/*****************************************************************************
 * MouseEvent: callback for mouse events
 *****************************************************************************/
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
675
    VLC_UNUSED(oldval); VLC_UNUSED(newval);
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
    filter_sys_t *p_sys = (filter_sys_t *)p_data;
    vout_thread_t *p_vout = (vout_thread_t*)p_sys->p_vout;
    int i_x, i_y;
    int i_v;

#define MOUSE_DOWN    1
#define MOUSE_CLICKED 2
#define MOUSE_MOVE_X  4
#define MOUSE_MOVE_Y  8
#define MOUSE_MOVE    12
    uint8_t mouse= 0;

    int v_h = p_vout->output.i_height;
    int v_w = p_vout->output.i_width;

    if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X;
    if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y;
    if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED;

    i_v = var_GetInteger( p_sys->p_vout, "mouse-button-down" );
    if( i_v & 0x1 ) mouse |= MOUSE_DOWN;
    i_y = var_GetInteger( p_sys->p_vout, "mouse-y" );
    i_x = var_GetInteger( p_sys->p_vout, "mouse-x" );

    if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
        return VLC_SUCCESS;

    if( mouse & MOUSE_CLICKED )
    {
        int i_scale_width, i_scale_height;
        osd_button_t *p_button = NULL;

        i_scale_width = p_vout->fmt_out.i_visible_width * 1000 /
            p_vout->fmt_in.i_visible_width;
        i_scale_height = p_vout->fmt_out.i_visible_height * 1000 /
            p_vout->fmt_in.i_visible_height;

        p_button = osd_ButtonFind( p_this, i_x, i_y, v_h, v_w,
                                   i_scale_width, i_scale_height );
        if( p_button )
        {
            osd_ButtonSelect( p_this, p_button );
718
719
            p_sys->b_update = p_sys->b_visible ? true : false;
            p_sys->b_clicked = true;
720
721
722
723
724
            msg_Dbg( p_this, "mouse clicked %s (%d,%d)\n", p_button->psz_name, i_x, i_y );
        }
    }
    return VLC_SUCCESS;
}