1 /*****************************************************************************
2 * marq.c : marquee display video plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2003-2005 the VideoLAN team
7 * Authors: Mark Moriarty
8 * Sigmund Augdal Helberg <dnumgis@videolan.org>
9 * Antoine Cellerier <dionoea . videolan \ org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
29 #include <stdlib.h> /* malloc(), free() */
37 #include "vlc_filter.h"
38 #include "vlc_block.h"
40 #include "vlc_playlist.h"
42 #include "vlc_input.h"
45 /*****************************************************************************
47 *****************************************************************************/
48 static int CreateFilter ( vlc_object_t * );
49 static void DestroyFilter( vlc_object_t * );
50 static subpicture_t *Filter( filter_t *, mtime_t );
53 static int MarqueeCallback( vlc_object_t *p_this, char const *psz_var,
54 vlc_value_t oldval, vlc_value_t newval,
56 static int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
57 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
58 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
59 0x00000080, 0x000000FF, 0x0000FFFF};
60 static char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
61 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
62 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
63 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
66 /*****************************************************************************
67 * filter_sys_t: marquee filter descriptor
68 *****************************************************************************/
71 int i_xoff, i_yoff; /* offsets for the display string in the video window */
72 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
75 char *psz_marquee; /* marquee string */
77 text_style_t *p_style; /* font control */
81 vlc_bool_t b_need_update;
84 #define MSG_TEXT N_("Text")
85 #define MSG_LONGTEXT N_( \
86 "Marquee text to display. " \
87 "(Available format strings: " \
88 "Time related: %Y = year, %m = month, %d = day, %H = hour, " \
89 "%M = minute, %S = second, ... " \
90 "Meta data related: $a = artist, $b = album, $c = copyright, " \
91 "$d = description, $e = encoded by, $g = genre, " \
92 "$l = language, $n = track num, $p = now playing, " \
93 "$r = rating, $s = subtitles language, $t = title, "\
94 "$u = url, $A = date, " \
95 "$B = audio bitrate (in kb/s), $C = chapter," \
96 "$D = duration, $F = full name with path, $I = title, "\
98 "$N = name, $O = audio language, $P = position (in %), $R = rate, " \
99 "$S = audio sample rate (in kHz), " \
100 "$T = time, $U = publisher, $V = volume, $_ = new line) ")
101 #define POSX_TEXT N_("X offset")
102 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
103 #define POSY_TEXT N_("Y offset")
104 #define POSY_LONGTEXT N_("Y offset, down from the top." )
105 #define TIMEOUT_TEXT N_("Timeout")
106 #define TIMEOUT_LONGTEXT N_("Number of milliseconds the marquee must remain " \
107 "displayed. Default value is " \
108 "0 (remains forever).")
109 #define OPACITY_TEXT N_("Opacity")
110 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
111 "overlayed text. 0 = transparent, 255 = totally opaque. " )
112 #define SIZE_TEXT N_("Font size, pixels")
113 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
116 #define COLOR_TEXT N_("Color")
117 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
118 "the video. This must be an hexadecimal (like HTML colors). The first two "\
119 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
120 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
122 #define POS_TEXT N_("Marquee position")
123 #define POS_LONGTEXT N_( \
124 "You can enforce the marquee position on the video " \
125 "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
126 "also use combinations of these values, eg 6 = top-right).")
128 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
129 static char *ppsz_pos_descriptions[] =
130 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
131 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
133 #define CFG_PREFIX "marq-"
135 /*****************************************************************************
137 *****************************************************************************/
139 set_capability( "sub filter", 0 );
140 set_shortname( N_("Marquee" ));
141 set_callbacks( CreateFilter, DestroyFilter );
142 set_category( CAT_VIDEO );
143 set_subcategory( SUBCAT_VIDEO_SUBPIC );
144 add_string( CFG_PREFIX "marquee", "VLC", NULL, MSG_TEXT, MSG_LONGTEXT,
147 set_section( N_("Position"), NULL );
148 add_integer( CFG_PREFIX "x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
149 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
150 add_integer( CFG_PREFIX "position", 5, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
152 set_section( N_("Font"), NULL );
153 /* 5 sets the default to top [1] left [4] */
154 change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
155 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
156 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
157 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
159 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
160 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT,
163 set_section( N_("Misc"), NULL );
164 add_integer( CFG_PREFIX "timeout", 0, NULL, TIMEOUT_TEXT, TIMEOUT_LONGTEXT,
167 set_description( _("Marquee display") );
168 add_shortcut( "marq" );
169 add_shortcut( "time" );
172 static const char *ppsz_filter_options[] = {
173 "marquee", "x", "y", "position", "color", "size", "timeout", NULL
176 /*****************************************************************************
177 * CreateFilter: allocates marquee video filter
178 *****************************************************************************/
179 static int CreateFilter( vlc_object_t *p_this )
181 filter_t *p_filter = (filter_t *)p_this;
184 /* Allocate structure */
185 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
188 msg_Err( p_filter, "out of memory" );
192 p_sys->p_style = malloc( sizeof( text_style_t ) );
193 memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ) );
195 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
198 #define CREATE_VAR( stor, type, var ) \
199 p_sys->stor = var_CreateGet##type( p_filter, var ); \
200 var_AddCallback( p_filter, var, MarqueeCallback, p_sys );
202 CREATE_VAR( i_xoff, Integer, "marq-x" );
203 CREATE_VAR( i_yoff, Integer, "marq-y" );
204 CREATE_VAR( i_timeout,Integer, "marq-timeout" );
205 CREATE_VAR( i_pos, Integer, "marq-position" );
206 CREATE_VAR( psz_marquee, String, "marq-marquee" );
207 CREATE_VAR( p_style->i_font_alpha, Integer, "marq-opacity" );
208 CREATE_VAR( p_style->i_font_color, Integer, "marq-color" );
209 CREATE_VAR( p_style->i_font_size, Integer, "marq-size" );
211 p_sys->p_style->i_font_alpha = 255 - p_sys->p_style->i_font_alpha ;
214 p_filter->pf_sub_filter = Filter;
215 p_sys->last_time = ((time_t)-1);
216 p_sys->b_need_update = VLC_TRUE;
220 /*****************************************************************************
221 * DestroyFilter: destroy marquee video filter
222 *****************************************************************************/
223 static void DestroyFilter( vlc_object_t *p_this )
225 filter_t *p_filter = (filter_t *)p_this;
226 filter_sys_t *p_sys = p_filter->p_sys;
228 if( p_sys->p_style ) free( p_sys->p_style );
229 if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
232 /* Delete the marquee variables */
233 #define DEL_VAR(var) \
234 var_DelCallback( p_filter, var, MarqueeCallback, p_sys ); \
235 var_Destroy( p_filter, var );
238 DEL_VAR( "marq-marquee" );
239 DEL_VAR( "marq-timeout" );
240 DEL_VAR( "marq-position" );
241 DEL_VAR( "marq-color" );
242 DEL_VAR( "marq-opacity" );
243 DEL_VAR( "marq-size" );
245 /****************************************************************************
246 * String formating functions
247 ****************************************************************************/
249 static char *FormatTime(char *tformat )
253 #if defined(HAVE_LOCALTIME_R)
259 /* Get the current time. */
260 curtime = time( NULL );
262 /* Convert it to local time representation. */
263 #if defined(HAVE_LOCALTIME_R)
264 localtime_r( &curtime, &loctime );
265 strftime( buffer, 255, tformat, &loctime );
267 loctime = localtime( &curtime );
268 strftime( buffer, 255, tformat, loctime );
270 return strdup( buffer );
273 #define INSERT_STRING( check, string ) \
274 if( check && string ) \
276 int len = strlen( string ); \
277 dst = realloc( dst, \
278 i_size = i_size + len + 1 ); \
279 strncpy( d, string, len+1 ); \
287 char *FormatMeta( vlc_object_t *p_object, char *string )
290 char *dst = malloc( 1000 );
294 int i_size = strlen( string );
296 playlist_t *p_playlist = pl_Yield( p_object );
297 input_thread_t *p_input = p_playlist->p_input;
298 input_item_t *p_item = NULL;
299 pl_Release( p_object );
302 vlc_object_yield( p_input );
303 p_item = p_input->input.p_item;
305 vlc_mutex_lock( &p_item->lock );
308 sprintf( dst, string );
317 INSERT_STRING( p_item && p_item->p_meta,
318 p_item->p_meta->psz_artist );
321 INSERT_STRING( p_item && p_item->p_meta,
322 p_item->p_meta->psz_album );
325 INSERT_STRING( p_item && p_item->p_meta,
326 p_item->p_meta->psz_copyright );
329 INSERT_STRING( p_item && p_item->p_meta,
330 p_item->p_meta->psz_description );
333 INSERT_STRING( p_item && p_item->p_meta,
334 p_item->p_meta->psz_encodedby );
337 INSERT_STRING( p_item && p_item->p_meta,
338 p_item->p_meta->psz_genre );
341 INSERT_STRING( p_item && p_item->p_meta,
342 p_item->p_meta->psz_language );
345 INSERT_STRING( p_item && p_item->p_meta,
346 p_item->p_meta->psz_tracknum );
349 INSERT_STRING( p_item && p_item->p_meta,
350 p_item->p_meta->psz_nowplaying );
353 INSERT_STRING( p_item && p_item->p_meta,
354 p_item->p_meta->psz_rating );
361 lang = var_GetString( p_input, "sub-language" );
365 lang = strdup( "-" );
367 INSERT_STRING( 1, lang );
372 INSERT_STRING( p_item && p_item->p_meta,
373 p_item->p_meta->psz_title );
376 INSERT_STRING( p_item && p_item->p_meta,
377 p_item->p_meta->psz_url );
380 INSERT_STRING( p_item && p_item->p_meta,
381 p_item->p_meta->psz_date );
386 snprintf( buf, 10, "%d",
387 var_GetInteger( p_input, "bit-rate" )/1000 );
393 INSERT_STRING( 1, buf );
398 snprintf( buf, 10, "%d",
399 var_GetInteger( p_input, "chapter" ) );
405 INSERT_STRING( 1, buf );
410 sprintf( buf, "%02d:%02d:%02d",
411 (int)(p_item->i_duration/(3600000000)),
412 (int)((p_item->i_duration/(60000000))%60),
413 (int)((p_item->i_duration/1000000)%60) );
417 sprintf( buf, "--:--:--" );
419 INSERT_STRING( 1, buf );
422 INSERT_STRING( p_item, p_item->psz_uri );
427 snprintf( buf, 10, "%d",
428 var_GetInteger( p_input, "title" ) );
434 INSERT_STRING( 1, buf );
437 if( p_item && p_input )
439 sprintf( buf, "%02d:%02d:%02d",
440 (int)((p_item->i_duration-p_input->i_time)/(3600000000)),
441 (int)(((p_item->i_duration-p_input->i_time)/(60000000))%60),
442 (int)(((p_item->i_duration-p_input->i_time)/1000000)%60) );
446 sprintf( buf, "--:--:--" );
448 INSERT_STRING( 1, buf );
451 INSERT_STRING( p_item, p_item->psz_name );
458 lang = var_GetString( p_input, "audio-language" );
462 lang = strdup( "-" );
464 INSERT_STRING( 1, lang );
471 snprintf( buf, 10, "%2.1lf",
472 var_GetFloat( p_input, "position" ) * 100. );
476 sprintf( buf, "--.-%%" );
478 INSERT_STRING( 1, buf );
483 int r = var_GetInteger( p_input, "rate" );
484 snprintf( buf, 10, "%d.%d", r/1000, r%1000 );
490 INSERT_STRING( 1, buf );
495 int r = var_GetInteger( p_input, "sample-rate" );
496 snprintf( buf, 10, "%d.%d", r/1000, (r/100)%10 );
502 INSERT_STRING( 1, buf );
507 sprintf( buf, "%02d:%02d:%02d",
508 (int)(p_input->i_time/(3600000000)),
509 (int)((p_input->i_time/(60000000))%60),
510 (int)((p_input->i_time/1000000)%60) );
514 sprintf( buf, "--:--:--" );
516 INSERT_STRING( 1, buf );
519 INSERT_STRING( p_item && p_item->p_meta,
520 p_item->p_meta->psz_publisher );
524 audio_volume_t volume;
525 aout_VolumeGet( p_object, &volume );
526 snprintf( buf, 10, "%d", volume );
527 INSERT_STRING( 1, buf );
557 vlc_object_release( p_input );
559 vlc_mutex_unlock( &p_item->lock );
565 /****************************************************************************
566 * Filter: the whole thing
567 ****************************************************************************
568 * This function outputs subpictures at regular time intervals.
569 ****************************************************************************/
570 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
572 filter_sys_t *p_sys = p_filter->p_sys;
578 if( p_sys->last_time == time( NULL ) )
583 if( p_sys->b_need_update == VLC_FALSE )
588 p_spu = p_filter->pf_sub_buffer_new( p_filter );
589 if( !p_spu ) return NULL;
591 memset( &fmt, 0, sizeof(video_format_t) );
592 fmt.i_chroma = VLC_FOURCC('T','E','X','T');
594 fmt.i_width = fmt.i_height = 0;
597 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
598 if( !p_spu->p_region )
600 p_filter->pf_sub_buffer_del( p_filter, p_spu );
604 t = p_sys->last_time = time( NULL );
606 if( strchr( p_sys->psz_marquee, '%' ) || strchr( p_sys->psz_marquee, '$' ) )
608 p_sys->b_need_update = VLC_TRUE;
612 p_sys->b_need_update = VLC_FALSE;
614 buf = FormatTime( p_sys->psz_marquee );
615 p_spu->p_region->psz_text = FormatMeta( VLC_OBJECT( p_filter ), buf );
617 p_spu->i_start = date;
618 p_spu->i_stop = p_sys->i_timeout == 0 ? 0 : date + p_sys->i_timeout * 1000;
619 p_spu->b_ephemer = VLC_TRUE;
621 /* where to locate the string: */
622 if( p_sys->i_xoff < 0 || p_sys->i_yoff < 0 )
623 { /* set to one of the 9 relative locations */
624 p_spu->i_flags = p_sys->i_pos;
627 p_spu->b_absolute = VLC_FALSE;
630 { /* set to an absolute xy, referenced to upper left corner */
631 p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
632 p_spu->i_x = p_sys->i_xoff;
633 p_spu->i_y = p_sys->i_yoff;
634 p_spu->b_absolute = VLC_TRUE;
636 p_spu->p_region->p_style = p_sys->p_style;
641 /**********************************************************************
642 * Callback to update params on the fly
643 **********************************************************************/
644 static int MarqueeCallback( vlc_object_t *p_this, char const *psz_var,
645 vlc_value_t oldval, vlc_value_t newval,
648 filter_sys_t *p_sys = (filter_sys_t *) p_data;
650 if( !strncmp( psz_var, "marq-marquee", 7 ) )
652 if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
653 p_sys->psz_marquee = strdup( newval.psz_string );
655 else if ( !strncmp( psz_var, "marq-x", 6 ) )
657 p_sys->i_xoff = newval.i_int;
659 else if ( !strncmp( psz_var, "marq-y", 6 ) )
661 p_sys->i_yoff = newval.i_int;
663 else if ( !strncmp( psz_var, "marq-color", 8 ) ) /* "marq-col" */
665 p_sys->p_style->i_font_color = newval.i_int;
667 else if ( !strncmp( psz_var, "marq-opacity", 8 ) ) /* "marq-opa" */
669 p_sys->p_style->i_font_alpha = 255 - newval.i_int;
671 else if ( !strncmp( psz_var, "marq-size", 6 ) )
673 p_sys->p_style->i_font_size = newval.i_int;
675 else if ( !strncmp( psz_var, "marq-timeout", 12 ) )
677 p_sys->i_timeout = newval.i_int;
679 else if ( !strncmp( psz_var, "marq-position", 8 ) )
680 /* willing to accept a match against marq-pos */
682 p_sys->i_pos = newval.i_int;
683 p_sys->i_xoff = -1; /* force to relative positioning */
685 p_sys->b_need_update = VLC_TRUE;