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"
44 /*****************************************************************************
46 *****************************************************************************/
47 static int CreateFilter ( vlc_object_t * );
48 static void DestroyFilter( vlc_object_t * );
49 static subpicture_t *Filter( filter_t *, mtime_t );
52 static int MarqueeCallback( vlc_object_t *p_this, char const *psz_var,
53 vlc_value_t oldval, vlc_value_t newval,
55 static int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
56 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
57 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
58 0x00000080, 0x000000FF, 0x0000FFFF};
59 static char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
60 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
61 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
62 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
65 /*****************************************************************************
66 * filter_sys_t: marquee filter descriptor
67 *****************************************************************************/
70 int i_xoff, i_yoff; /* offsets for the display string in the video window */
71 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
74 char *psz_marquee; /* marquee string */
76 text_style_t *p_style; /* font control */
80 vlc_bool_t b_need_update;
83 #define MSG_TEXT N_("Text")
84 #define MSG_LONGTEXT N_( \
85 "Marquee text to display. " \
86 "(Available format strings: " \
87 "Time related: %Y = year, %m = month, %d = day, %H = hour, " \
88 "%M = minute, %S = second, ... " \
89 "Meta data related: $a = artist, $b = album, $c = copyright, " \
90 "$d = description, $e = encoded by, $g = genre, " \
91 "$l = language, $n = track num, $p = now playing, " \
92 "$r = rating, $t = title, $u = url, $A = date, " \
93 "$D = duration, $F = full name with path, $L = time left, " \
94 "$N = name, $P = publisher, $T = time, $_ = new line) ")
95 #define POSX_TEXT N_("X offset")
96 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
97 #define POSY_TEXT N_("Y offset")
98 #define POSY_LONGTEXT N_("Y offset, down from the top." )
99 #define TIMEOUT_TEXT N_("Timeout")
100 #define TIMEOUT_LONGTEXT N_("Number of milliseconds the marquee must remain " \
101 "displayed. Default value is " \
102 "0 (remains forever).")
103 #define OPACITY_TEXT N_("Opacity")
104 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
105 "overlayed text. 0 = transparent, 255 = totally opaque. " )
106 #define SIZE_TEXT N_("Font size, pixels")
107 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
110 #define COLOR_TEXT N_("Color")
111 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
112 "the video. This must be an hexadecimal (like HTML colors). The first two "\
113 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
114 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
116 #define POS_TEXT N_("Marquee position")
117 #define POS_LONGTEXT N_( \
118 "You can enforce the marquee position on the video " \
119 "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
120 "also use combinations of these values, eg 6 = top-right).")
122 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
123 static char *ppsz_pos_descriptions[] =
124 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
125 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
127 /*****************************************************************************
129 *****************************************************************************/
131 set_capability( "sub filter", 0 );
132 set_shortname( N_("Marquee" ));
133 set_callbacks( CreateFilter, DestroyFilter );
134 set_category( CAT_VIDEO );
135 set_subcategory( SUBCAT_VIDEO_SUBPIC );
136 add_string( "marq-marquee", "VLC", NULL, MSG_TEXT, MSG_LONGTEXT,
139 set_section( N_("Position"), NULL );
140 add_integer( "marq-x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
141 add_integer( "marq-y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
142 add_integer( "marq-position", 5, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
144 set_section( N_("Font"), NULL );
145 /* 5 sets the default to top [1] left [4] */
146 change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
147 add_integer_with_range( "marq-opacity", 255, 0, 255, NULL,
148 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
149 add_integer( "marq-color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
151 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
152 add_integer( "marq-size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE );
154 set_section( N_("Misc"), NULL );
155 add_integer( "marq-timeout", 0, NULL, TIMEOUT_TEXT, TIMEOUT_LONGTEXT,
158 set_description( _("Marquee display") );
159 add_shortcut( "marq" );
160 add_shortcut( "time" );
163 /*****************************************************************************
164 * CreateFilter: allocates marquee video filter
165 *****************************************************************************/
166 static int CreateFilter( vlc_object_t *p_this )
168 filter_t *p_filter = (filter_t *)p_this;
171 /* Allocate structure */
172 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
175 msg_Err( p_filter, "out of memory" );
179 p_sys->p_style = malloc( sizeof( text_style_t ) );
180 memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ) );
182 #define CREATE_VAR( stor, type, var ) \
183 p_sys->stor = var_CreateGet##type( p_filter->p_libvlc, var ); \
184 var_AddCallback( p_filter->p_libvlc, var, MarqueeCallback, p_sys );
186 CREATE_VAR( i_xoff, Integer, "marq-x" );
187 CREATE_VAR( i_yoff, Integer, "marq-y" );
188 CREATE_VAR( i_timeout,Integer, "marq-timeout" );
189 CREATE_VAR( i_pos, Integer, "marq-position" );
190 CREATE_VAR( psz_marquee, String, "marq-marquee" );
191 CREATE_VAR( p_style->i_font_alpha, Integer, "marq-opacity" );
192 CREATE_VAR( p_style->i_font_color, Integer, "marq-color" );
193 CREATE_VAR( p_style->i_font_size, Integer, "marq-size" );
195 p_sys->p_style->i_font_alpha = 255 - p_sys->p_style->i_font_alpha ;
198 p_filter->pf_sub_filter = Filter;
199 p_sys->last_time = ((time_t)-1);
200 p_sys->b_need_update = VLC_TRUE;
204 /*****************************************************************************
205 * DestroyFilter: destroy marquee video filter
206 *****************************************************************************/
207 static void DestroyFilter( vlc_object_t *p_this )
209 filter_t *p_filter = (filter_t *)p_this;
210 filter_sys_t *p_sys = p_filter->p_sys;
212 if( p_sys->p_style ) free( p_sys->p_style );
213 if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
216 /* Delete the marquee variables */
217 #define DEL_VAR(var) \
218 var_DelCallback( p_filter->p_libvlc, var, MarqueeCallback, p_sys ); \
219 var_Destroy( p_filter->p_libvlc, var );
222 DEL_VAR( "marq-marquee" );
223 DEL_VAR( "marq-timeout" );
224 DEL_VAR( "marq-position" );
225 DEL_VAR( "marq-color" );
226 DEL_VAR( "marq-opacity" );
227 DEL_VAR( "marq-size" );
229 /****************************************************************************
230 * String formating functions
231 ****************************************************************************/
233 static char *FormatTime(char *tformat )
237 #if defined(HAVE_LOCALTIME_R)
243 /* Get the current time. */
244 curtime = time( NULL );
246 /* Convert it to local time representation. */
247 #if defined(HAVE_LOCALTIME_R)
248 localtime_r( &curtime, &loctime );
249 strftime( buffer, 255, tformat, &loctime );
251 loctime = localtime( &curtime );
252 strftime( buffer, 255, tformat, loctime );
254 return strdup( buffer );
257 #define INSERT_STRING( check, string ) \
258 if( check && string ) \
260 int len = strlen( string ); \
261 dst = realloc( dst, \
262 i_size = i_size + len + 1 ); \
263 strncpy( d, string, len+1 ); \
271 char *FormatMeta( vlc_object_t *p_object, char *string )
274 char *dst = malloc( 1000 );
278 int i_size = strlen( string );
280 playlist_t *p_playlist = pl_Yield( p_object );
281 input_thread_t *p_input = p_playlist->p_input;
282 input_item_t *p_item = NULL;
283 pl_Release( p_object );
286 vlc_object_yield( p_input );
287 p_item = p_input->input.p_item;
289 vlc_mutex_lock( &p_item->lock );
292 sprintf( dst, string );
301 INSERT_STRING( p_item && p_item->p_meta,
302 p_item->p_meta->psz_artist );
305 INSERT_STRING( p_item && p_item->p_meta,
306 p_item->p_meta->psz_album );
309 INSERT_STRING( p_item && p_item->p_meta,
310 p_item->p_meta->psz_copyright );
313 INSERT_STRING( p_item && p_item->p_meta,
314 p_item->p_meta->psz_description );
317 INSERT_STRING( p_item && p_item->p_meta,
318 p_item->p_meta->psz_encodedby );
321 INSERT_STRING( p_item && p_item->p_meta,
322 p_item->p_meta->psz_genre );
325 INSERT_STRING( p_item && p_item->p_meta,
326 p_item->p_meta->psz_language );
329 INSERT_STRING( p_item && p_item->p_meta,
330 p_item->p_meta->psz_tracknum );
333 INSERT_STRING( p_item && p_item->p_meta,
334 p_item->p_meta->psz_nowplaying );
337 INSERT_STRING( p_item && p_item->p_meta,
338 p_item->p_meta->psz_rating );
341 INSERT_STRING( p_item && p_item->p_meta,
342 p_item->p_meta->psz_title );
345 INSERT_STRING( p_item && p_item->p_meta,
346 p_item->p_meta->psz_url );
349 INSERT_STRING( p_item && p_item->p_meta,
350 p_item->p_meta->psz_date );
355 sprintf( buf, "%02d:%02d:%02d",
356 (int)(p_item->i_duration/(3600000000)),
357 (int)((p_item->i_duration/(60000000))%60),
358 (int)((p_item->i_duration/1000000)%60) );
362 sprintf( buf, "--:--:--" );
364 INSERT_STRING( 1, buf );
367 INSERT_STRING( p_item, p_item->psz_uri );
370 if( p_item && p_input )
372 sprintf( buf, "%02d:%02d:%02d",
373 (int)((p_item->i_duration-p_input->i_time)/(3600000000)),
374 (int)(((p_item->i_duration-p_input->i_time)/(60000000))%60),
375 (int)(((p_item->i_duration-p_input->i_time)/1000000)%60) );
379 sprintf( buf, "--:--:--" );
381 INSERT_STRING( 1, buf );
384 INSERT_STRING( p_item, p_item->psz_name );
387 INSERT_STRING( p_item && p_item->p_meta,
388 p_item->p_meta->psz_publisher );
393 sprintf( buf, "%02d:%02d:%02d",
394 (int)(p_input->i_time/(3600000000)),
395 (int)((p_input->i_time/(60000000))%60),
396 (int)((p_input->i_time/1000000)%60) );
400 sprintf( buf, "--:--:--" );
402 INSERT_STRING( 1, buf );
431 vlc_object_release( p_input );
433 vlc_mutex_unlock( &p_item->lock );
439 /****************************************************************************
440 * Filter: the whole thing
441 ****************************************************************************
442 * This function outputs subpictures at regular time intervals.
443 ****************************************************************************/
444 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
446 filter_sys_t *p_sys = p_filter->p_sys;
452 if( p_sys->last_time == time( NULL ) )
457 /* if( p_sys->b_need_update == VLC_FALSE )
462 p_spu = p_filter->pf_sub_buffer_new( p_filter );
463 if( !p_spu ) return NULL;
465 memset( &fmt, 0, sizeof(video_format_t) );
466 fmt.i_chroma = VLC_FOURCC('T','E','X','T');
468 fmt.i_width = fmt.i_height = 0;
471 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
472 if( !p_spu->p_region )
474 p_filter->pf_sub_buffer_del( p_filter, p_spu );
478 t = p_sys->last_time = time( NULL );
480 buf = FormatTime( p_sys->psz_marquee );
481 p_spu->p_region->psz_text = FormatMeta( VLC_OBJECT( p_filter ), buf );
483 p_spu->i_start = date;
484 p_spu->i_stop = p_sys->i_timeout == 0 ? 0 : date + p_sys->i_timeout * 1000;
485 p_spu->b_ephemer = VLC_TRUE;
487 /* where to locate the string: */
488 if( p_sys->i_xoff < 0 || p_sys->i_yoff < 0 )
489 { /* set to one of the 9 relative locations */
490 p_spu->i_flags = p_sys->i_pos;
493 p_spu->b_absolute = VLC_FALSE;
496 { /* set to an absolute xy, referenced to upper left corner */
497 p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
498 p_spu->i_x = p_sys->i_xoff;
499 p_spu->i_y = p_sys->i_yoff;
500 p_spu->b_absolute = VLC_TRUE;
502 p_spu->p_region->p_style = p_sys->p_style;
504 p_sys->b_need_update = VLC_FALSE;
508 /**********************************************************************
509 * Callback to update params on the fly
510 **********************************************************************/
511 static int MarqueeCallback( vlc_object_t *p_this, char const *psz_var,
512 vlc_value_t oldval, vlc_value_t newval,
515 filter_sys_t *p_sys = (filter_sys_t *) p_data;
517 if( !strncmp( psz_var, "marq-marquee", 7 ) )
519 if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
520 p_sys->psz_marquee = strdup( newval.psz_string );
522 else if ( !strncmp( psz_var, "marq-x", 6 ) )
524 p_sys->i_xoff = newval.i_int;
526 else if ( !strncmp( psz_var, "marq-y", 6 ) )
528 p_sys->i_yoff = newval.i_int;
530 else if ( !strncmp( psz_var, "marq-color", 8 ) ) /* "marq-col" */
532 p_sys->p_style->i_font_color = newval.i_int;
534 else if ( !strncmp( psz_var, "marq-opacity", 8 ) ) /* "marq-opa" */
536 p_sys->p_style->i_font_alpha = 255 - newval.i_int;
538 else if ( !strncmp( psz_var, "marq-size", 6 ) )
540 p_sys->p_style->i_font_size = newval.i_int;
542 else if ( !strncmp( psz_var, "marq-timeout", 12 ) )
544 p_sys->i_timeout = newval.i_int;
546 else if ( !strncmp( psz_var, "marq-position", 8 ) )
547 /* willing to accept a match against marq-pos */
549 p_sys->i_pos = newval.i_int;
550 p_sys->i_xoff = -1; /* force to relative positioning */
552 p_sys->b_need_update = VLC_TRUE;