]> git.sesse.net Git - vlc/blob - modules/video_filter/marq.c
Some more marq format strings
[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 #include <vlc/aout.h>
44
45 /*****************************************************************************
46  * Local prototypes
47  *****************************************************************************/
48 static int  CreateFilter ( vlc_object_t * );
49 static void DestroyFilter( vlc_object_t * );
50 static subpicture_t *Filter( filter_t *, mtime_t );
51
52
53 static int MarqueeCallback( vlc_object_t *p_this, char const *psz_var,
54                             vlc_value_t oldval, vlc_value_t newval,
55                             void *p_data );
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"),
64                N_("Aqua") };
65
66 /*****************************************************************************
67  * filter_sys_t: marquee filter descriptor
68  *****************************************************************************/
69 struct filter_sys_t
70 {
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) */
73     int i_timeout;
74
75     char *psz_marquee;    /* marquee string */
76
77     text_style_t *p_style; /* font control */
78
79     time_t last_time;
80
81     vlc_bool_t b_need_update;
82 };
83
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, $t = title, $u = url, $A = date, " \
94     "$B = audio bitrate (in kb/s), $C = chapter," \
95     "$D = duration, $F = full name with path, $I = title, "\
96     "$L = time left, " \
97     "$N = name, $P = position (in %), $S = audio sample rate (in kHz), " \
98     "$T = time, $U = publisher, $V = volume, $_ = new line) ")
99 #define POSX_TEXT N_("X offset")
100 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
101 #define POSY_TEXT N_("Y offset")
102 #define POSY_LONGTEXT N_("Y offset, down from the top." )
103 #define TIMEOUT_TEXT N_("Timeout")
104 #define TIMEOUT_LONGTEXT N_("Number of milliseconds the marquee must remain " \
105                             "displayed. Default value is " \
106                             "0 (remains forever).")
107 #define OPACITY_TEXT N_("Opacity")
108 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
109     "overlayed text. 0 = transparent, 255 = totally opaque. " )
110 #define SIZE_TEXT N_("Font size, pixels")
111 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
112     "font size)." )
113
114 #define COLOR_TEXT N_("Color")
115 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
116     "the video. This must be an hexadecimal (like HTML colors). The first two "\
117     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
118     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
119
120 #define POS_TEXT N_("Marquee position")
121 #define POS_LONGTEXT N_( \
122   "You can enforce the marquee position on the video " \
123   "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
124   "also use combinations of these values, eg 6 = top-right).")
125
126 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
127 static char *ppsz_pos_descriptions[] =
128      { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
129      N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
130
131 /*****************************************************************************
132  * Module descriptor
133  *****************************************************************************/
134 vlc_module_begin();
135     set_capability( "sub filter", 0 );
136     set_shortname( N_("Marquee" ));
137     set_callbacks( CreateFilter, DestroyFilter );
138     set_category( CAT_VIDEO );
139     set_subcategory( SUBCAT_VIDEO_SUBPIC );
140     add_string( "marq-marquee", "VLC", NULL, MSG_TEXT, MSG_LONGTEXT,
141                 VLC_FALSE );
142
143     set_section( N_("Position"), NULL );
144     add_integer( "marq-x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
145     add_integer( "marq-y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
146     add_integer( "marq-position", 5, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
147
148     set_section( N_("Font"), NULL );
149     /* 5 sets the default to top [1] left [4] */
150     change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
151     add_integer_with_range( "marq-opacity", 255, 0, 255, NULL,
152         OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
153     add_integer( "marq-color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
154                   VLC_FALSE );
155         change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
156     add_integer( "marq-size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE );
157
158     set_section( N_("Misc"), NULL );
159     add_integer( "marq-timeout", 0, NULL, TIMEOUT_TEXT, TIMEOUT_LONGTEXT,
160                  VLC_FALSE );
161
162     set_description( _("Marquee display") );
163     add_shortcut( "marq" );
164     add_shortcut( "time" );
165 vlc_module_end();
166
167 /*****************************************************************************
168  * CreateFilter: allocates marquee video filter
169  *****************************************************************************/
170 static int CreateFilter( vlc_object_t *p_this )
171 {
172     filter_t *p_filter = (filter_t *)p_this;
173     filter_sys_t *p_sys;
174
175     /* Allocate structure */
176     p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
177     if( p_sys == NULL )
178     {
179         msg_Err( p_filter, "out of memory" );
180         return VLC_ENOMEM;
181     }
182
183     p_sys->p_style = malloc( sizeof( text_style_t ) );
184     memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ) );
185
186 #define CREATE_VAR( stor, type, var ) \
187     p_sys->stor = var_CreateGet##type( p_filter->p_libvlc, var ); \
188     var_AddCallback( p_filter->p_libvlc, var, MarqueeCallback, p_sys );
189
190     CREATE_VAR( i_xoff, Integer, "marq-x" );
191     CREATE_VAR( i_yoff, Integer, "marq-y" );
192     CREATE_VAR( i_timeout,Integer, "marq-timeout" );
193     CREATE_VAR( i_pos, Integer, "marq-position" );
194     CREATE_VAR( psz_marquee, String, "marq-marquee" );
195     CREATE_VAR( p_style->i_font_alpha, Integer, "marq-opacity" );
196     CREATE_VAR( p_style->i_font_color, Integer, "marq-color" );
197     CREATE_VAR( p_style->i_font_size, Integer, "marq-size" );
198
199     p_sys->p_style->i_font_alpha = 255 - p_sys->p_style->i_font_alpha ;
200
201     /* Misc init */
202     p_filter->pf_sub_filter = Filter;
203     p_sys->last_time = ((time_t)-1);
204     p_sys->b_need_update = VLC_TRUE;
205
206     return VLC_SUCCESS;
207 }
208 /*****************************************************************************
209  * DestroyFilter: destroy marquee video filter
210  *****************************************************************************/
211 static void DestroyFilter( vlc_object_t *p_this )
212 {
213     filter_t *p_filter = (filter_t *)p_this;
214     filter_sys_t *p_sys = p_filter->p_sys;
215
216     if( p_sys->p_style ) free( p_sys->p_style );
217     if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
218     free( p_sys );
219
220     /* Delete the marquee variables */
221 #define DEL_VAR(var) \
222     var_DelCallback( p_filter->p_libvlc, var, MarqueeCallback, p_sys ); \
223     var_Destroy( p_filter->p_libvlc, var );
224     DEL_VAR( "marq-x" );
225     DEL_VAR( "marq-y" );
226     DEL_VAR( "marq-marquee" );
227     DEL_VAR( "marq-timeout" );
228     DEL_VAR( "marq-position" );
229     DEL_VAR( "marq-color" );
230     DEL_VAR( "marq-opacity" );
231     DEL_VAR( "marq-size" );
232 }
233 /****************************************************************************
234  * String formating functions
235  ****************************************************************************/
236
237 static char *FormatTime(char *tformat )
238 {
239     char buffer[255];
240     time_t curtime;
241 #if defined(HAVE_LOCALTIME_R)
242     struct tm loctime;
243 #else
244     struct tm *loctime;
245 #endif
246
247     /* Get the current time.  */
248     curtime = time( NULL );
249
250     /* Convert it to local time representation.  */
251 #if defined(HAVE_LOCALTIME_R)
252     localtime_r( &curtime, &loctime );
253     strftime( buffer, 255, tformat, &loctime );
254 #else
255     loctime = localtime( &curtime );
256     strftime( buffer, 255, tformat, loctime );
257 #endif
258     return strdup( buffer );
259 }
260
261 #define INSERT_STRING( check, string )                              \
262                     if( check && string )                           \
263                     {                                               \
264                         int len = strlen( string );                 \
265                         dst = realloc( dst,                         \
266                                        i_size = i_size + len + 1 ); \
267                         strncpy( d, string, len+1 );                \
268                         d += len;                                   \
269                     }                                               \
270                     else                                            \
271                     {                                               \
272                         *d = '-';                                   \
273                         d++;                                        \
274                     }
275 char *FormatMeta( vlc_object_t *p_object, char *string )
276 {
277     char *s = string;
278     char *dst = malloc( 1000 );
279     char *d = dst;
280     int b_is_format = 0;
281     char buf[10];
282     int i_size = strlen( string );
283
284     playlist_t *p_playlist = pl_Yield( p_object );
285     input_thread_t *p_input = p_playlist->p_input;
286     input_item_t *p_item = NULL;
287     pl_Release( p_object );
288     if( p_input )
289     {
290         vlc_object_yield( p_input );
291         p_item = p_input->input.p_item;
292         if( p_item )
293             vlc_mutex_lock( &p_item->lock );
294     }
295
296     sprintf( dst, string );
297
298     while( *s )
299     {
300         if( b_is_format )
301         {
302             switch( *s )
303             {
304                 case 'a':
305                     INSERT_STRING( p_item && p_item->p_meta,
306                                    p_item->p_meta->psz_artist );
307                     break;
308                 case 'b':
309                     INSERT_STRING( p_item && p_item->p_meta,
310                                    p_item->p_meta->psz_album );
311                     break;
312                 case 'c':
313                     INSERT_STRING( p_item && p_item->p_meta,
314                                    p_item->p_meta->psz_copyright );
315                     break;
316                 case 'd':
317                     INSERT_STRING( p_item && p_item->p_meta,
318                                    p_item->p_meta->psz_description );
319                     break;
320                 case 'e':
321                     INSERT_STRING( p_item && p_item->p_meta,
322                                    p_item->p_meta->psz_encodedby );
323                     break;
324                 case 'g':
325                     INSERT_STRING( p_item && p_item->p_meta,
326                                    p_item->p_meta->psz_genre );
327                     break;
328                 case 'l':
329                     INSERT_STRING( p_item && p_item->p_meta,
330                                    p_item->p_meta->psz_language );
331                     break;
332                 case 'n':
333                     INSERT_STRING( p_item && p_item->p_meta,
334                                    p_item->p_meta->psz_tracknum );
335                     break;
336                 case 'p':
337                     INSERT_STRING( p_item && p_item->p_meta,
338                                    p_item->p_meta->psz_nowplaying );
339                     break;
340                 case 'r':
341                     INSERT_STRING( p_item && p_item->p_meta,
342                                    p_item->p_meta->psz_rating );
343                     break;
344                 case 't':
345                     INSERT_STRING( p_item && p_item->p_meta,
346                                    p_item->p_meta->psz_title );
347                     break;
348                 case 'u':
349                     INSERT_STRING( p_item && p_item->p_meta,
350                                    p_item->p_meta->psz_url );
351                     break;
352                 case 'A':
353                     INSERT_STRING( p_item && p_item->p_meta,
354                                    p_item->p_meta->psz_date );
355                     break;
356                 case 'B':
357                     if( p_input )
358                     {
359                         snprintf( buf, 10, "%d",
360                                   var_GetInteger( p_input, "bit-rate" )/1000 );
361                     }
362                     else
363                     {
364                         sprintf( buf, "-" );
365                     }
366                     INSERT_STRING( 1, buf );
367                     break;
368                 case 'C':
369                     if( p_input )
370                     {
371                         snprintf( buf, 10, "%d",
372                                   var_GetInteger( p_input, "chapter" ) );
373                     }
374                     else
375                     {
376                         sprintf( buf, "-" );
377                     }
378                     INSERT_STRING( 1, buf );
379                     break;
380                 case 'D':
381                     if( p_item )
382                     {
383                         sprintf( buf, "%02d:%02d:%02d",
384                                  (int)(p_item->i_duration/(3600000000)),
385                                  (int)((p_item->i_duration/(60000000))%60),
386                                  (int)((p_item->i_duration/1000000)%60) );
387                     }
388                     else
389                     {
390                         sprintf( buf, "--:--:--" );
391                     }
392                     INSERT_STRING( 1, buf );
393                     break;
394                 case 'F':
395                     INSERT_STRING( p_item, p_item->psz_uri );
396                     break;
397                 case 'I':
398                     if( p_input )
399                     {
400                         snprintf( buf, 10, "%d",
401                                   var_GetInteger( p_input, "title" ) );
402                     }
403                     else
404                     {
405                         sprintf( buf, "-" );
406                     }
407                     INSERT_STRING( 1, buf );
408                     break;
409                 case 'L':
410                     if( p_item && p_input )
411                     {
412                         sprintf( buf, "%02d:%02d:%02d",
413                      (int)((p_item->i_duration-p_input->i_time)/(3600000000)),
414                      (int)(((p_item->i_duration-p_input->i_time)/(60000000))%60),
415                      (int)(((p_item->i_duration-p_input->i_time)/1000000)%60) );
416                     }
417                     else
418                     {
419                         sprintf( buf, "--:--:--" );
420                     }
421                     INSERT_STRING( 1, buf );
422                     break;
423                 case 'N':
424                     INSERT_STRING( p_item, p_item->psz_name );
425                     break;
426                 case 'P':
427                     if( p_input )
428                     {
429                         snprintf( buf, 10, "%2.1lf",
430                                   var_GetFloat( p_input, "position" ) * 100. );
431                     }
432                     else
433                     {
434                         sprintf( buf, "--.-%%" );
435                     }
436                     INSERT_STRING( 1, buf );
437                     break;
438                 case 'S':
439                     if( p_input )
440                     {
441                         int r = var_GetInteger( p_input, "sample-rate" );
442                         snprintf( buf, 10, "%d.%d", r/1000, (r/100)%10 );
443                     }
444                     else
445                     {
446                         sprintf( buf, "-" );
447                     }
448                     INSERT_STRING( 1, buf );
449                     break;
450                 case 'T':
451                     if( p_input )
452                     {
453                         sprintf( buf, "%02d:%02d:%02d",
454                                  (int)(p_input->i_time/(3600000000)),
455                                  (int)((p_input->i_time/(60000000))%60),
456                                  (int)((p_input->i_time/1000000)%60) );
457                     }
458                     else
459                     {
460                         sprintf( buf, "--:--:--" );
461                     }
462                     INSERT_STRING( 1, buf );
463                     break;
464                 case 'U':
465                     INSERT_STRING( p_item && p_item->p_meta,
466                                    p_item->p_meta->psz_publisher );
467                     break;
468                 case 'V':
469                 {
470                     audio_volume_t volume;
471                     aout_VolumeGet( p_object, &volume );
472                     snprintf( buf, 10, "%d", volume );
473                     INSERT_STRING( 1, buf );
474                     break;
475                 }
476                 case '_':
477                     *d = '\n';
478                     d++;
479                     break;
480
481                 default:
482                     *d = *s;
483                     d++;
484                     break;
485             }
486             b_is_format = 0;
487         }
488         else if( *s == '$' )
489         {
490             b_is_format = 1;
491         }
492         else
493         {
494             *d = *s;
495             d++;
496         }
497         s++;
498     }
499     *d = '\0';
500
501     if( p_input )
502     {
503         vlc_object_release( p_input );
504         if( p_item )
505             vlc_mutex_unlock( &p_item->lock );
506     }
507
508     return dst;
509 }
510
511 /****************************************************************************
512  * Filter: the whole thing
513  ****************************************************************************
514  * This function outputs subpictures at regular time intervals.
515  ****************************************************************************/
516 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
517 {
518     filter_sys_t *p_sys = p_filter->p_sys;
519     subpicture_t *p_spu;
520     video_format_t fmt;
521     time_t t;
522     char *buf;
523
524     if( p_sys->last_time == time( NULL ) )
525     {
526         return NULL;
527     }
528
529 /*    if( p_sys->b_need_update == VLC_FALSE )
530     {
531         return NULL;
532     }*/
533
534     p_spu = p_filter->pf_sub_buffer_new( p_filter );
535     if( !p_spu ) return NULL;
536
537     memset( &fmt, 0, sizeof(video_format_t) );
538     fmt.i_chroma = VLC_FOURCC('T','E','X','T');
539     fmt.i_aspect = 0;
540     fmt.i_width = fmt.i_height = 0;
541     fmt.i_x_offset = 0;
542     fmt.i_y_offset = 0;
543     p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
544     if( !p_spu->p_region )
545     {
546         p_filter->pf_sub_buffer_del( p_filter, p_spu );
547         return NULL;
548     }
549
550     t = p_sys->last_time = time( NULL );
551
552     buf = FormatTime( p_sys->psz_marquee );
553     p_spu->p_region->psz_text = FormatMeta( VLC_OBJECT( p_filter ), buf );
554     free( buf );
555     p_spu->i_start = date;
556     p_spu->i_stop  = p_sys->i_timeout == 0 ? 0 : date + p_sys->i_timeout * 1000;
557     p_spu->b_ephemer = VLC_TRUE;
558
559     /*  where to locate the string: */
560     if( p_sys->i_xoff < 0 || p_sys->i_yoff < 0 )
561     {   /* set to one of the 9 relative locations */
562         p_spu->i_flags = p_sys->i_pos;
563         p_spu->i_x = 0;
564         p_spu->i_y = 0;
565         p_spu->b_absolute = VLC_FALSE;
566     }
567     else
568     {   /*  set to an absolute xy, referenced to upper left corner */
569         p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
570         p_spu->i_x = p_sys->i_xoff;
571         p_spu->i_y = p_sys->i_yoff;
572         p_spu->b_absolute = VLC_TRUE;
573     }
574     p_spu->p_region->p_style = p_sys->p_style;
575
576     p_sys->b_need_update = VLC_FALSE;
577     return p_spu;
578 }
579
580 /**********************************************************************
581  * Callback to update params on the fly
582  **********************************************************************/
583 static int MarqueeCallback( vlc_object_t *p_this, char const *psz_var,
584                             vlc_value_t oldval, vlc_value_t newval,
585                             void *p_data )
586 {
587     filter_sys_t *p_sys = (filter_sys_t *) p_data;
588
589     if( !strncmp( psz_var, "marq-marquee", 7 ) )
590     {
591         if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
592         p_sys->psz_marquee = strdup( newval.psz_string );
593     }
594     else if ( !strncmp( psz_var, "marq-x", 6 ) )
595     {
596         p_sys->i_xoff = newval.i_int;
597     }
598     else if ( !strncmp( psz_var, "marq-y", 6 ) )
599     {
600         p_sys->i_yoff = newval.i_int;
601     }
602     else if ( !strncmp( psz_var, "marq-color", 8 ) )  /* "marq-col" */
603     {
604         p_sys->p_style->i_font_color = newval.i_int;
605     }
606     else if ( !strncmp( psz_var, "marq-opacity", 8 ) ) /* "marq-opa" */
607     {
608         p_sys->p_style->i_font_alpha = 255 - newval.i_int;
609     }
610     else if ( !strncmp( psz_var, "marq-size", 6 ) )
611     {
612         p_sys->p_style->i_font_size = newval.i_int;
613     }
614     else if ( !strncmp( psz_var, "marq-timeout", 12 ) )
615     {
616         p_sys->i_timeout = newval.i_int;
617     }
618     else if ( !strncmp( psz_var, "marq-position", 8 ) )
619     /* willing to accept a match against marq-pos */
620     {
621         p_sys->i_pos = newval.i_int;
622         p_sys->i_xoff = -1;       /* force to relative positioning */
623     }
624     p_sys->b_need_update = VLC_TRUE;
625     return VLC_SUCCESS;
626 }