marq.c 14.1 KB
Newer Older
1 2 3
/*****************************************************************************
 * marq.c : marquee display video plugin for vlc
 *****************************************************************************
4
 * Copyright (C) 2003-2008 the VideoLAN team
dionoea's avatar
dionoea committed
5
 * $Id$
6 7
 *
 * Authors: Mark Moriarty
8 9
 *          Sigmund Augdal Helberg <dnumgis@videolan.org>
 *          Antoine Cellerier <dionoea . videolan \ org>
10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 25 26 27 28 29
 *****************************************************************************/

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

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

34
#include <vlc_common.h>
35
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
36
#include <vlc_vout.h>
37 38 39

#include "vlc_filter.h"
#include "vlc_block.h"
40
#include "vlc_osd.h"
41 42

#include "vlc_strings.h"
43 44 45 46 47 48 49 50 51 52 53 54

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int  CreateFilter ( vlc_object_t * );
static void DestroyFilter( vlc_object_t * );
static subpicture_t *Filter( filter_t *, mtime_t );


static int MarqueeCallback( vlc_object_t *p_this, char const *psz_var,
                            vlc_value_t oldval, vlc_value_t newval,
                            void *p_data );
55 56
static const int pi_color_values[] = {
               0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
dionoea's avatar
dionoea committed
57 58 59
               0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
               0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
               0x00000080, 0x000000FF, 0x0000FFFF};
60 61 62
static const char *const ppsz_color_descriptions[] = {
               N_("Default"), N_("Black"), N_("Gray"),
               N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
dionoea's avatar
dionoea committed
63 64
               N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
               N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
65 66 67 68 69 70 71 72 73 74 75 76 77
               N_("Aqua") };

/*****************************************************************************
 * filter_sys_t: marquee filter descriptor
 *****************************************************************************/
struct filter_sys_t
{
    int i_xoff, i_yoff;  /* offsets for the display string in the video window */
    int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
    int i_timeout;

    char *psz_marquee;    /* marquee string */

hartman's avatar
hartman committed
78
    text_style_t *p_style; /* font control */
dionoea's avatar
dionoea committed
79

80 81
    mtime_t last_time;
    mtime_t i_refresh;
82

83
    bool b_need_update;
84 85
};

86
#define MSG_TEXT N_("Text")
87 88 89 90 91 92 93 94
#define MSG_LONGTEXT N_( \
    "Marquee text to display. " \
    "(Available format strings: " \
    "Time related: %Y = year, %m = month, %d = day, %H = hour, " \
    "%M = minute, %S = second, ... " \
    "Meta data related: $a = artist, $b = album, $c = copyright, " \
    "$d = description, $e = encoded by, $g = genre, " \
    "$l = language, $n = track num, $p = now playing, " \
dionoea's avatar
dionoea committed
95 96
    "$r = rating, $s = subtitles language, $t = title, "\
    "$u = url, $A = date, " \
dionoea's avatar
dionoea committed
97 98 99
    "$B = audio bitrate (in kb/s), $C = chapter," \
    "$D = duration, $F = full name with path, $I = title, "\
    "$L = time left, " \
dionoea's avatar
dionoea committed
100 101
    "$N = name, $O = audio language, $P = position (in %), $R = rate, " \
    "$S = audio sample rate (in kHz), " \
dionoea's avatar
dionoea committed
102
    "$T = time, $U = publisher, $V = volume, $_ = new line) ")
103
#define POSX_TEXT N_("X offset")
104
#define POSX_LONGTEXT N_("X offset, from the left screen edge." )
105
#define POSY_TEXT N_("Y offset")
106
#define POSY_LONGTEXT N_("Y offset, down from the top." )
107 108
#define TIMEOUT_TEXT N_("Timeout")
#define TIMEOUT_LONGTEXT N_("Number of milliseconds the marquee must remain " \
109
                            "displayed. Default value is " \
110
                            "0 (remains forever).")
111 112 113 114
#define REFRESH_TEXT N_("Refresh period in ms")
#define REFRESH_LONGTEXT N_("Number of milliseconds between string updates. " \
                            "This is mainly usefull when using meta data " \
                            "or time format string sequences.")
zorglub's avatar
zorglub committed
115
#define OPACITY_TEXT N_("Opacity")
116
#define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
117
    "overlayed text. 0 = transparent, 255 = totally opaque. " )
118
#define SIZE_TEXT N_("Font size, pixels")
119 120
#define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
    "font size)." )
121

122 123 124 125 126
#define COLOR_TEXT N_("Color")
#define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
    "the video. This must be an hexadecimal (like HTML colors). The first two "\
    "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
    " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
127 128 129 130 131

#define POS_TEXT N_("Marquee position")
#define POS_LONGTEXT N_( \
  "You can enforce the marquee position on the video " \
  "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
132
  "also use combinations of these values, eg 6 = top-right).")
133

134 135
static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
static const char *const ppsz_pos_descriptions[] =
136 137 138
     { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
     N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };

dionoea's avatar
 
dionoea committed
139 140
#define CFG_PREFIX "marq-"

141 142 143 144 145
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
vlc_module_begin();
    set_capability( "sub filter", 0 );
146
    set_shortname( N_("Marquee" ));
147 148 149
    set_callbacks( CreateFilter, DestroyFilter );
    set_category( CAT_VIDEO );
    set_subcategory( SUBCAT_VIDEO_SUBPIC );
dionoea's avatar
 
dionoea committed
150
    add_string( CFG_PREFIX "marquee", "VLC", NULL, MSG_TEXT, MSG_LONGTEXT,
151
                false );
zorglub's avatar
zorglub committed
152 153

    set_section( N_("Position"), NULL );
154 155 156
    add_integer( CFG_PREFIX "x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, true );
    add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, true );
    add_integer( CFG_PREFIX "position", -1, NULL, POS_TEXT, POS_LONGTEXT, false );
157
        change_integer_list( pi_pos_values, ppsz_pos_descriptions, NULL );
zorglub's avatar
zorglub committed
158 159

    set_section( N_("Font"), NULL );
160
    /* 5 sets the default to top [1] left [4] */
dionoea's avatar
 
dionoea committed
161
    add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
162
        OPACITY_TEXT, OPACITY_LONGTEXT, false );
dionoea's avatar
 
dionoea committed
163
    add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
164
                 false );
165
        change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
dionoea's avatar
 
dionoea committed
166
    add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT,
167
                 false );
168

zorglub's avatar
zorglub committed
169
    set_section( N_("Misc"), NULL );
dionoea's avatar
 
dionoea committed
170
    add_integer( CFG_PREFIX "timeout", 0, NULL, TIMEOUT_TEXT, TIMEOUT_LONGTEXT,
171
                 false );
172
    add_integer( CFG_PREFIX "refresh", 1000, NULL, REFRESH_TEXT,
173
                 REFRESH_LONGTEXT, false );
zorglub's avatar
zorglub committed
174

175
    set_description( N_("Marquee display") );
176
    add_shortcut( "time" );
177 178 179 180 181 182 183
    add_obsolete_string( "time-format" );
    add_obsolete_string( "time-x" );
    add_obsolete_string( "time-y" );
    add_obsolete_string( "time-position" );
    add_obsolete_string( "time-opacity" );
    add_obsolete_string( "time-color" );
    add_obsolete_string( "time-size" );
184 185
vlc_module_end();

186
static const char *const ppsz_filter_options[] = {
187 188
    "marquee", "x", "y", "position", "color", "size", "timeout", "refresh",
    NULL
dionoea's avatar
 
dionoea committed
189 190
};

191 192 193 194 195 196 197 198 199 200 201 202 203
/*****************************************************************************
 * CreateFilter: allocates marquee video filter
 *****************************************************************************/
static int CreateFilter( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t *)p_this;
    filter_sys_t *p_sys;

    /* Allocate structure */
    p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
    if( p_sys == NULL )
        return VLC_ENOMEM;

hartman's avatar
hartman committed
204 205 206
    p_sys->p_style = malloc( sizeof( text_style_t ) );
    memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ) );

dionoea's avatar
 
dionoea committed
207 208 209
    config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
                       p_filter->p_cfg );

210
#define CREATE_VAR( stor, type, var ) \
211
    p_sys->stor = var_CreateGet##type##Command( p_filter, var ); \
dionoea's avatar
 
dionoea committed
212
    var_AddCallback( p_filter, var, MarqueeCallback, p_sys );
213 214 215 216

    CREATE_VAR( i_xoff, Integer, "marq-x" );
    CREATE_VAR( i_yoff, Integer, "marq-y" );
    CREATE_VAR( i_timeout,Integer, "marq-timeout" );
217
    CREATE_VAR( i_refresh,Integer, "marq-refresh" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
218
    p_sys->i_refresh *= 1000;
219 220 221 222 223 224 225
    CREATE_VAR( i_pos, Integer, "marq-position" );
    CREATE_VAR( psz_marquee, String, "marq-marquee" );
    CREATE_VAR( p_style->i_font_alpha, Integer, "marq-opacity" );
    CREATE_VAR( p_style->i_font_color, Integer, "marq-color" );
    CREATE_VAR( p_style->i_font_size, Integer, "marq-size" );

    p_sys->p_style->i_font_alpha = 255 - p_sys->p_style->i_font_alpha ;
226 227 228

    /* Misc init */
    p_filter->pf_sub_filter = Filter;
229
    p_sys->last_time = 0;
230
    p_sys->b_need_update = true;
231 232 233 234 235 236 237 238 239 240 241

    return VLC_SUCCESS;
}
/*****************************************************************************
 * DestroyFilter: destroy marquee 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;

ivoire's avatar
ivoire committed
242 243
    free( p_sys->p_style );
    free( p_sys->psz_marquee );
244 245

    /* Delete the marquee variables */
246
#define DEL_VAR(var) \
dionoea's avatar
 
dionoea committed
247 248
    var_DelCallback( p_filter, var, MarqueeCallback, p_sys ); \
    var_Destroy( p_filter, var );
249 250 251 252 253 254 255 256
    DEL_VAR( "marq-x" );
    DEL_VAR( "marq-y" );
    DEL_VAR( "marq-marquee" );
    DEL_VAR( "marq-timeout" );
    DEL_VAR( "marq-position" );
    DEL_VAR( "marq-color" );
    DEL_VAR( "marq-opacity" );
    DEL_VAR( "marq-size" );
257 258

    free( p_sys );
259 260 261 262 263 264 265 266 267 268
}

/****************************************************************************
 * Filter: the whole thing
 ****************************************************************************
 * This function outputs subpictures at regular time intervals.
 ****************************************************************************/
static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
{
    filter_sys_t *p_sys = p_filter->p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
269
    subpicture_t *p_spu = NULL;
270 271
    video_format_t fmt;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
272
    if( p_sys->last_time + p_sys->i_refresh > date )
273 274
        return NULL;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
275
    vlc_object_lock( p_filter );
276
    if( p_sys->b_need_update == false )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
277
        goto out;
278

Laurent Aimar's avatar
Laurent Aimar committed
279
    p_spu = filter_NewSubpicture( p_filter );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
280 281
    if( !p_spu )
        goto out;
282 283 284 285 286 287 288 289 290 291 292

    memset( &fmt, 0, sizeof(video_format_t) );
    fmt.i_chroma = VLC_FOURCC('T','E','X','T');
    fmt.i_aspect = 0;
    fmt.i_width = fmt.i_height = 0;
    fmt.i_x_offset = 0;
    fmt.i_y_offset = 0;
    p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
    if( !p_spu->p_region )
    {
        p_filter->pf_sub_buffer_del( p_filter, p_spu );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
293 294
        p_spu = NULL;
        goto out;
295 296
    }

297
    p_sys->last_time = date;
298

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
299 300
    if( !strchr( p_sys->psz_marquee, '%' )
     && !strchr( p_sys->psz_marquee, '$' ) )
301
        p_sys->b_need_update = false;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
302

303
    p_spu->p_region->psz_text = str_format( p_filter, p_sys->psz_marquee );
304 305
    p_spu->i_start = date;
    p_spu->i_stop  = p_sys->i_timeout == 0 ? 0 : date + p_sys->i_timeout * 1000;
306
    p_spu->b_ephemer = true;
307 308

    /*  where to locate the string: */
309
    if( p_sys->i_pos < 0 )
dionoea's avatar
dionoea committed
310
    {   /*  set to an absolute xy */
Laurent Aimar's avatar
Laurent Aimar committed
311
        p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
312
        p_spu->b_absolute = true;
313
    }
314
    else
dionoea's avatar
dionoea committed
315
    {   /* set to one of the 9 relative locations */
316
        p_spu->p_region->i_align = p_sys->i_pos;
317
        p_spu->b_absolute = false;
318 319
    }

320 321
    p_spu->p_region->i_x = p_sys->i_xoff;
    p_spu->p_region->i_y = p_sys->i_yoff;
322

hartman's avatar
hartman committed
323
    p_spu->p_region->p_style = p_sys->p_style;
324

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
325 326
out:
    vlc_object_unlock( p_filter );
327 328 329 330 331 332 333 334 335 336
    return p_spu;
}

/**********************************************************************
 * Callback to update params on the fly
 **********************************************************************/
static int MarqueeCallback( vlc_object_t *p_this, char const *psz_var,
                            vlc_value_t oldval, vlc_value_t newval,
                            void *p_data )
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
337
    VLC_UNUSED(oldval);
338 339
    filter_sys_t *p_sys = (filter_sys_t *) p_data;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
340
    vlc_object_lock( p_this );
341 342
    if( !strncmp( psz_var, "marq-marquee", 7 ) )
    {
ivoire's avatar
ivoire committed
343
        free( p_sys->psz_marquee );
344 345 346 347 348 349 350 351 352 353
        p_sys->psz_marquee = strdup( newval.psz_string );
    }
    else if ( !strncmp( psz_var, "marq-x", 6 ) )
    {
        p_sys->i_xoff = newval.i_int;
    }
    else if ( !strncmp( psz_var, "marq-y", 6 ) )
    {
        p_sys->i_yoff = newval.i_int;
    }
dionoea's avatar
dionoea committed
354
    else if ( !strncmp( psz_var, "marq-color", 8 ) )  /* "marq-col" */
355
    {
hartman's avatar
hartman committed
356
        p_sys->p_style->i_font_color = newval.i_int;
357
    }
dionoea's avatar
dionoea committed
358
    else if ( !strncmp( psz_var, "marq-opacity", 8 ) ) /* "marq-opa" */
359
    {
hartman's avatar
hartman committed
360
        p_sys->p_style->i_font_alpha = 255 - newval.i_int;
361 362 363
    }
    else if ( !strncmp( psz_var, "marq-size", 6 ) )
    {
hartman's avatar
hartman committed
364
        p_sys->p_style->i_font_size = newval.i_int;
365 366 367 368 369
    }
    else if ( !strncmp( psz_var, "marq-timeout", 12 ) )
    {
        p_sys->i_timeout = newval.i_int;
    }
370 371
    else if ( !strncmp( psz_var, "marq-refresh", 12 ) )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
372
        p_sys->i_refresh = newval.i_int * 1000;
373
    }
374 375 376 377 378 379
    else if ( !strncmp( psz_var, "marq-position", 8 ) )
    /* willing to accept a match against marq-pos */
    {
        p_sys->i_pos = newval.i_int;
        p_sys->i_xoff = -1;       /* force to relative positioning */
    }
380
    p_sys->b_need_update = true;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
381
    vlc_object_unlock( p_this );
382 383
    return VLC_SUCCESS;
}