]> git.sesse.net Git - vlc/blob - modules/video_filter/marq.c
Add $_ line skip variable.
[vlc] / modules / video_filter / marq.c
1 /*****************************************************************************
2  * marq.c : marquee display video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Mark Moriarty
8  *          Sigmund Augdal Helberg <dnumgis@videolan.org>
9  *          Antoine Cellerier <dionoea . videolan \ org>
10  *
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.
15  *
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.
20  *
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  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>                                      /* malloc(), free() */
30 #include <string.h>
31
32 #include <time.h>
33
34 #include <vlc/vlc.h>
35 #include <vlc/vout.h>
36
37 #include "vlc_filter.h"
38 #include "vlc_block.h"
39 #include "vlc_osd.h"
40 #include "vlc_playlist.h"
41 #include "vlc_meta.h"
42 #include "vlc_input.h"
43
44 /*****************************************************************************
45  * Local prototypes
46  *****************************************************************************/
47 static int  CreateFilter ( vlc_object_t * );
48 static void DestroyFilter( vlc_object_t * );
49 static subpicture_t *Filter( filter_t *, mtime_t );
50
51
52 static int MarqueeCallback( vlc_object_t *p_this, char const *psz_var,
53                             vlc_value_t oldval, vlc_value_t newval,
54                             void *p_data );
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"),
63                N_("Aqua") };
64
65 /*****************************************************************************
66  * filter_sys_t: marquee filter descriptor
67  *****************************************************************************/
68 struct filter_sys_t
69 {
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) */
72     int i_timeout;
73
74     char *psz_marquee;    /* marquee string */
75
76     text_style_t *p_style; /* font control */
77
78     time_t last_time;
79
80     vlc_bool_t b_need_update;
81 };
82
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 " \
108     "font size)." )
109
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" )
115
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).")
121
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") };
126
127 /*****************************************************************************
128  * Module descriptor
129  *****************************************************************************/
130 vlc_module_begin();
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,
137                 VLC_FALSE );
138
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 );
143
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,
150                   VLC_FALSE );
151         change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
152     add_integer( "marq-size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE );
153
154     set_section( N_("Misc"), NULL );
155     add_integer( "marq-timeout", 0, NULL, TIMEOUT_TEXT, TIMEOUT_LONGTEXT,
156                  VLC_FALSE );
157
158     set_description( _("Marquee display") );
159     add_shortcut( "marq" );
160     add_shortcut( "time" );
161 vlc_module_end();
162
163 /*****************************************************************************
164  * CreateFilter: allocates marquee video filter
165  *****************************************************************************/
166 static int CreateFilter( vlc_object_t *p_this )
167 {
168     filter_t *p_filter = (filter_t *)p_this;
169     filter_sys_t *p_sys;
170
171     /* Allocate structure */
172     p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
173     if( p_sys == NULL )
174     {
175         msg_Err( p_filter, "out of memory" );
176         return VLC_ENOMEM;
177     }
178
179     p_sys->p_style = malloc( sizeof( text_style_t ) );
180     memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ) );
181
182     p_sys->i_xoff = var_CreateGetInteger( p_filter->p_libvlc_global , "marq-x" );
183     p_sys->i_yoff = var_CreateGetInteger( p_filter->p_libvlc_global , "marq-y" );
184     p_sys->i_timeout = var_CreateGetInteger( p_filter->p_libvlc_global , "marq-timeout" );
185     p_sys->i_pos = var_CreateGetInteger( p_filter->p_libvlc_global , "marq-position" );
186     p_sys->psz_marquee =  var_CreateGetString( p_filter->p_libvlc_global, "marq-marquee" );
187     p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter->p_libvlc_global , "marq-opacity" );
188     p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter->p_libvlc_global , "marq-color" );
189     p_sys->p_style->i_font_size  = var_CreateGetInteger( p_filter->p_libvlc_global , "marq-size" );
190
191     var_AddCallback( p_filter->p_libvlc_global, "marq-x", MarqueeCallback, p_sys );
192     var_AddCallback( p_filter->p_libvlc_global, "marq-y", MarqueeCallback, p_sys );
193     var_AddCallback( p_filter->p_libvlc_global, "marq-marquee", MarqueeCallback, p_sys );
194     var_AddCallback( p_filter->p_libvlc_global, "marq-timeout", MarqueeCallback, p_sys );
195     var_AddCallback( p_filter->p_libvlc_global, "marq-position", MarqueeCallback, p_sys );
196     var_AddCallback( p_filter->p_libvlc_global, "marq-color", MarqueeCallback, p_sys );
197     var_AddCallback( p_filter->p_libvlc_global, "marq-opacity", MarqueeCallback, p_sys );
198     var_AddCallback( p_filter->p_libvlc_global, "marq-size", MarqueeCallback, p_sys );
199
200     /* Misc init */
201     p_filter->pf_sub_filter = Filter;
202     p_sys->last_time = ((time_t)-1);
203     p_sys->b_need_update = VLC_TRUE;
204
205     return VLC_SUCCESS;
206 }
207 /*****************************************************************************
208  * DestroyFilter: destroy marquee video filter
209  *****************************************************************************/
210 static void DestroyFilter( vlc_object_t *p_this )
211 {
212     filter_t *p_filter = (filter_t *)p_this;
213     filter_sys_t *p_sys = p_filter->p_sys;
214
215     if( p_sys->p_style ) free( p_sys->p_style );
216     if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
217     free( p_sys );
218
219     /* Delete the marquee variables */
220     var_DelCallback( p_filter->p_libvlc_global, "marq-x", MarqueeCallback, p_sys );
221     var_DelCallback( p_filter->p_libvlc_global, "marq-y", MarqueeCallback, p_sys );
222     var_DelCallback( p_filter->p_libvlc_global, "marq-marquee", MarqueeCallback, p_sys );
223     var_DelCallback( p_filter->p_libvlc_global, "marq-timeout", MarqueeCallback, p_sys );
224     var_DelCallback( p_filter->p_libvlc_global, "marq-position", MarqueeCallback, p_sys );
225     var_DelCallback( p_filter->p_libvlc_global, "marq-color", MarqueeCallback, p_sys );
226     var_DelCallback( p_filter->p_libvlc_global, "marq-opacity", MarqueeCallback, p_sys );
227     var_DelCallback( p_filter->p_libvlc_global, "marq-size", MarqueeCallback, p_sys );
228
229     var_Destroy( p_filter->p_libvlc_global , "marq-marquee" );
230     var_Destroy( p_filter->p_libvlc_global , "marq-x" );
231     var_Destroy( p_filter->p_libvlc_global , "marq-y" );
232     var_Destroy( p_filter->p_libvlc_global , "marq-timeout" );
233     var_Destroy( p_filter->p_libvlc_global , "marq-position" );
234     var_Destroy( p_filter->p_libvlc_global , "marq-color");
235     var_Destroy( p_filter->p_libvlc_global , "marq-opacity");
236     var_Destroy( p_filter->p_libvlc_global , "marq-size");
237 }
238 /****************************************************************************
239  * String formating functions
240  ****************************************************************************/
241
242 static char *FormatTime(char *tformat )
243 {
244     char buffer[255];
245     time_t curtime;
246 #if defined(HAVE_LOCALTIME_R)
247     struct tm loctime;
248 #else
249     struct tm *loctime;
250 #endif
251
252     /* Get the current time.  */
253     curtime = time( NULL );
254
255     /* Convert it to local time representation.  */
256 #if defined(HAVE_LOCALTIME_R)
257     localtime_r( &curtime, &loctime );
258     strftime( buffer, 255, tformat, &loctime );
259 #else
260     loctime = localtime( &curtime );
261     strftime( buffer, 255, tformat, loctime );
262 #endif
263     return strdup( buffer );
264 }
265
266 #define INSERT_STRING( check, string )                              \
267                     if( check && string )                           \
268                     {                                               \
269                         int len = strlen( string );                 \
270                         dst = realloc( dst,                         \
271                                        i_size = i_size + len + 1 ); \
272                         strncpy( d, string, len+1 );                \
273                         d += len;                                   \
274                     }                                               \
275                     else                                            \
276                     {                                               \
277                         *d = '-';                                   \
278                         d++;                                        \
279                     }
280 char *FormatMeta( vlc_object_t *p_object, char *string )
281 {
282     char *s = string;
283     char *dst = malloc( 1000 );
284     char *d = dst;
285     int b_is_format = 0;
286     char buf[10];
287     int i_size = strlen( string );
288
289     playlist_t *p_playlist = pl_Yield( p_object );
290     input_thread_t *p_input = p_playlist->p_input;
291     input_item_t *p_item = NULL;
292     pl_Release( p_object );
293     if( p_input )
294     {
295         vlc_object_yield( p_input );
296         p_item = p_input->input.p_item;
297         if( p_item )
298             vlc_mutex_lock( &p_item->lock );
299     }
300
301     sprintf( dst, string );
302
303     while( *s )
304     {
305         if( b_is_format )
306         {
307             switch( *s )
308             {
309                 case 'a':
310                     INSERT_STRING( p_item && p_item->p_meta,
311                                    p_item->p_meta->psz_artist );
312                     break;
313                 case 'b':
314                     INSERT_STRING( p_item && p_item->p_meta,
315                                    p_item->p_meta->psz_album );
316                     break;
317                 case 'c':
318                     INSERT_STRING( p_item && p_item->p_meta,
319                                    p_item->p_meta->psz_copyright );
320                     break;
321                 case 'd':
322                     INSERT_STRING( p_item && p_item->p_meta,
323                                    p_item->p_meta->psz_description );
324                     break;
325                 case 'e':
326                     INSERT_STRING( p_item && p_item->p_meta,
327                                    p_item->p_meta->psz_encodedby );
328                     break;
329                 case 'g':
330                     INSERT_STRING( p_item && p_item->p_meta,
331                                    p_item->p_meta->psz_genre );
332                     break;
333                 case 'l':
334                     INSERT_STRING( p_item && p_item->p_meta,
335                                    p_item->p_meta->psz_language );
336                     break;
337                 case 'n':
338                     INSERT_STRING( p_item && p_item->p_meta,
339                                    p_item->p_meta->psz_tracknum );
340                     break;
341                 case 'p':
342                     INSERT_STRING( p_item && p_item->p_meta,
343                                    p_item->p_meta->psz_nowplaying );
344                     break;
345                 case 'r':
346                     INSERT_STRING( p_item && p_item->p_meta,
347                                    p_item->p_meta->psz_rating );
348                     break;
349                 case 't':
350                     INSERT_STRING( p_item && p_item->p_meta,
351                                    p_item->p_meta->psz_title );
352                     break;
353                 case 'u':
354                     INSERT_STRING( p_item && p_item->p_meta,
355                                    p_item->p_meta->psz_url );
356                     break;
357                 case 'A':
358                     INSERT_STRING( p_item && p_item->p_meta,
359                                    p_item->p_meta->psz_date );
360                     break;
361                 case 'D':
362                     if( p_item )
363                     {
364                         sprintf( buf, "%02d:%02d:%02d",
365                                  (int)(p_item->i_duration/(3600000000)),
366                                  (int)((p_item->i_duration/(60000000))%60),
367                                  (int)((p_item->i_duration/1000000)%60) );
368                     }
369                     else
370                     {
371                         sprintf( buf, "--:--:--" );
372                     }
373                     INSERT_STRING( 1, buf );
374                     break;
375                 case 'F':
376                     INSERT_STRING( p_item, p_item->psz_uri );
377                     break;
378                 case 'L':
379                     if( p_item && p_input )
380                     {
381                         sprintf( buf, "%02d:%02d:%02d",
382                      (int)((p_item->i_duration-p_input->i_time)/(3600000000)),
383                      (int)(((p_item->i_duration-p_input->i_time)/(60000000))%60),
384                      (int)(((p_item->i_duration-p_input->i_time)/1000000)%60) );
385                     }
386                     else
387                     {
388                         sprintf( buf, "--:--:--" );
389                     }
390                     INSERT_STRING( 1, buf );
391                     break;
392                 case 'N':
393                     INSERT_STRING( p_item, p_item->psz_name );
394                     break;
395                 case 'P':
396                     INSERT_STRING( p_item && p_item->p_meta,
397                                    p_item->p_meta->psz_publisher );
398                     break;
399                 case 'T':
400                     if( p_input )
401                     {
402                         sprintf( buf, "%02d:%02d:%02d",
403                                  (int)(p_input->i_time/(3600000000)),
404                                  (int)((p_input->i_time/(60000000))%60),
405                                  (int)((p_input->i_time/1000000)%60) );
406                     }
407                     else
408                     {
409                         sprintf( buf, "--:--:--" );
410                     }
411                     INSERT_STRING( 1, buf );
412                     break;
413                 case '_':
414                     *d = '\n';
415                     d++;
416                     break;
417
418                 default:
419                     *d = *s;
420                     d++;
421                     break;
422             }
423             b_is_format = 0;
424         }
425         else if( *s == '$' )
426         {
427             b_is_format = 1;
428         }
429         else
430         {
431             *d = *s;
432             d++;
433         }
434         s++;
435     }
436     *d = '\0';
437
438     if( p_input )
439     {
440         vlc_object_release( p_input );
441         if( p_item )
442             vlc_mutex_unlock( &p_item->lock );
443     }
444
445     return dst;
446 }
447
448 /****************************************************************************
449  * Filter: the whole thing
450  ****************************************************************************
451  * This function outputs subpictures at regular time intervals.
452  ****************************************************************************/
453 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
454 {
455     filter_sys_t *p_sys = p_filter->p_sys;
456     subpicture_t *p_spu;
457     video_format_t fmt;
458     time_t t;
459     char *buf;
460
461     if( p_sys->last_time == time( NULL ) )
462     {
463         return NULL;
464     }
465
466 /*    if( p_sys->b_need_update == VLC_FALSE )
467     {
468         return NULL;
469     }*/
470
471     p_spu = p_filter->pf_sub_buffer_new( p_filter );
472     if( !p_spu ) return NULL;
473
474     memset( &fmt, 0, sizeof(video_format_t) );
475     fmt.i_chroma = VLC_FOURCC('T','E','X','T');
476     fmt.i_aspect = 0;
477     fmt.i_width = fmt.i_height = 0;
478     fmt.i_x_offset = 0;
479     fmt.i_y_offset = 0;
480     p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
481     if( !p_spu->p_region )
482     {
483         p_filter->pf_sub_buffer_del( p_filter, p_spu );
484         return NULL;
485     }
486
487     t = p_sys->last_time = time( NULL );
488
489     buf = FormatTime( p_sys->psz_marquee );
490     p_spu->p_region->psz_text = FormatMeta( VLC_OBJECT( p_filter ), buf );
491     free( buf );
492     p_spu->i_start = date;
493     p_spu->i_stop  = p_sys->i_timeout == 0 ? 0 : date + p_sys->i_timeout * 1000;
494     p_spu->b_ephemer = VLC_TRUE;
495
496     /*  where to locate the string: */
497     if( p_sys->i_xoff < 0 || p_sys->i_yoff < 0 )
498     {   /* set to one of the 9 relative locations */
499         p_spu->i_flags = p_sys->i_pos;
500         p_spu->i_x = 0;
501         p_spu->i_y = 0;
502         p_spu->b_absolute = VLC_FALSE;
503     }
504     else
505     {   /*  set to an absolute xy, referenced to upper left corner */
506         p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
507         p_spu->i_x = p_sys->i_xoff;
508         p_spu->i_y = p_sys->i_yoff;
509         p_spu->b_absolute = VLC_TRUE;
510     }
511     p_spu->p_region->p_style = p_sys->p_style;
512
513     p_sys->b_need_update = VLC_FALSE;
514     return p_spu;
515 }
516
517 /**********************************************************************
518  * Callback to update params on the fly
519  **********************************************************************/
520 static int MarqueeCallback( vlc_object_t *p_this, char const *psz_var,
521                             vlc_value_t oldval, vlc_value_t newval,
522                             void *p_data )
523 {
524     filter_sys_t *p_sys = (filter_sys_t *) p_data;
525
526     if( !strncmp( psz_var, "marq-marquee", 7 ) )
527     {
528         if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
529         p_sys->psz_marquee = strdup( newval.psz_string );
530     }
531     else if ( !strncmp( psz_var, "marq-x", 6 ) )
532     {
533         p_sys->i_xoff = newval.i_int;
534     }
535     else if ( !strncmp( psz_var, "marq-y", 6 ) )
536     {
537         p_sys->i_yoff = newval.i_int;
538     }
539     else if ( !strncmp( psz_var, "marq-color", 8 ) )  /* "marq-col" */
540     {
541         p_sys->p_style->i_font_color = newval.i_int;
542     }
543     else if ( !strncmp( psz_var, "marq-opacity", 8 ) ) /* "marq-opa" */
544     {
545         p_sys->p_style->i_font_alpha = 255 - newval.i_int;
546     }
547     else if ( !strncmp( psz_var, "marq-size", 6 ) )
548     {
549         p_sys->p_style->i_font_size = newval.i_int;
550     }
551     else if ( !strncmp( psz_var, "marq-timeout", 12 ) )
552     {
553         p_sys->i_timeout = newval.i_int;
554     }
555     else if ( !strncmp( psz_var, "marq-position", 8 ) )
556     /* willing to accept a match against marq-pos */
557     {
558         p_sys->i_pos = newval.i_int;
559         p_sys->i_xoff = -1;       /* force to relative positioning */
560     }
561     p_sys->b_need_update = VLC_TRUE;
562     return VLC_SUCCESS;
563 }