]> git.sesse.net Git - vlc/blob - modules/video_filter/marq.c
gra
[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 #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 );
185
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" );
194
195     p_sys->p_style->i_font_alpha = 255 - p_sys->p_style->i_font_alpha ;
196
197     /* Misc init */
198     p_filter->pf_sub_filter = Filter;
199     p_sys->last_time = ((time_t)-1);
200     p_sys->b_need_update = VLC_TRUE;
201
202     return VLC_SUCCESS;
203 }
204 /*****************************************************************************
205  * DestroyFilter: destroy marquee video filter
206  *****************************************************************************/
207 static void DestroyFilter( vlc_object_t *p_this )
208 {
209     filter_t *p_filter = (filter_t *)p_this;
210     filter_sys_t *p_sys = p_filter->p_sys;
211
212     if( p_sys->p_style ) free( p_sys->p_style );
213     if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
214     free( p_sys );
215
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 );
220     DEL_VAR( "marq-x" );
221     DEL_VAR( "marq-y" );
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" );
228 }
229 /****************************************************************************
230  * String formating functions
231  ****************************************************************************/
232
233 static char *FormatTime(char *tformat )
234 {
235     char buffer[255];
236     time_t curtime;
237 #if defined(HAVE_LOCALTIME_R)
238     struct tm loctime;
239 #else
240     struct tm *loctime;
241 #endif
242
243     /* Get the current time.  */
244     curtime = time( NULL );
245
246     /* Convert it to local time representation.  */
247 #if defined(HAVE_LOCALTIME_R)
248     localtime_r( &curtime, &loctime );
249     strftime( buffer, 255, tformat, &loctime );
250 #else
251     loctime = localtime( &curtime );
252     strftime( buffer, 255, tformat, loctime );
253 #endif
254     return strdup( buffer );
255 }
256
257 #define INSERT_STRING( check, string )                              \
258                     if( check && string )                           \
259                     {                                               \
260                         int len = strlen( string );                 \
261                         dst = realloc( dst,                         \
262                                        i_size = i_size + len + 1 ); \
263                         strncpy( d, string, len+1 );                \
264                         d += len;                                   \
265                     }                                               \
266                     else                                            \
267                     {                                               \
268                         *d = '-';                                   \
269                         d++;                                        \
270                     }
271 char *FormatMeta( vlc_object_t *p_object, char *string )
272 {
273     char *s = string;
274     char *dst = malloc( 1000 );
275     char *d = dst;
276     int b_is_format = 0;
277     char buf[10];
278     int i_size = strlen( string );
279
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 );
284     if( p_input )
285     {
286         vlc_object_yield( p_input );
287         p_item = p_input->input.p_item;
288         if( p_item )
289             vlc_mutex_lock( &p_item->lock );
290     }
291
292     sprintf( dst, string );
293
294     while( *s )
295     {
296         if( b_is_format )
297         {
298             switch( *s )
299             {
300                 case 'a':
301                     INSERT_STRING( p_item && p_item->p_meta,
302                                    p_item->p_meta->psz_artist );
303                     break;
304                 case 'b':
305                     INSERT_STRING( p_item && p_item->p_meta,
306                                    p_item->p_meta->psz_album );
307                     break;
308                 case 'c':
309                     INSERT_STRING( p_item && p_item->p_meta,
310                                    p_item->p_meta->psz_copyright );
311                     break;
312                 case 'd':
313                     INSERT_STRING( p_item && p_item->p_meta,
314                                    p_item->p_meta->psz_description );
315                     break;
316                 case 'e':
317                     INSERT_STRING( p_item && p_item->p_meta,
318                                    p_item->p_meta->psz_encodedby );
319                     break;
320                 case 'g':
321                     INSERT_STRING( p_item && p_item->p_meta,
322                                    p_item->p_meta->psz_genre );
323                     break;
324                 case 'l':
325                     INSERT_STRING( p_item && p_item->p_meta,
326                                    p_item->p_meta->psz_language );
327                     break;
328                 case 'n':
329                     INSERT_STRING( p_item && p_item->p_meta,
330                                    p_item->p_meta->psz_tracknum );
331                     break;
332                 case 'p':
333                     INSERT_STRING( p_item && p_item->p_meta,
334                                    p_item->p_meta->psz_nowplaying );
335                     break;
336                 case 'r':
337                     INSERT_STRING( p_item && p_item->p_meta,
338                                    p_item->p_meta->psz_rating );
339                     break;
340                 case 't':
341                     INSERT_STRING( p_item && p_item->p_meta,
342                                    p_item->p_meta->psz_title );
343                     break;
344                 case 'u':
345                     INSERT_STRING( p_item && p_item->p_meta,
346                                    p_item->p_meta->psz_url );
347                     break;
348                 case 'A':
349                     INSERT_STRING( p_item && p_item->p_meta,
350                                    p_item->p_meta->psz_date );
351                     break;
352                 case 'D':
353                     if( p_item )
354                     {
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) );
359                     }
360                     else
361                     {
362                         sprintf( buf, "--:--:--" );
363                     }
364                     INSERT_STRING( 1, buf );
365                     break;
366                 case 'F':
367                     INSERT_STRING( p_item, p_item->psz_uri );
368                     break;
369                 case 'L':
370                     if( p_item && p_input )
371                     {
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) );
376                     }
377                     else
378                     {
379                         sprintf( buf, "--:--:--" );
380                     }
381                     INSERT_STRING( 1, buf );
382                     break;
383                 case 'N':
384                     INSERT_STRING( p_item, p_item->psz_name );
385                     break;
386                 case 'P':
387                     INSERT_STRING( p_item && p_item->p_meta,
388                                    p_item->p_meta->psz_publisher );
389                     break;
390                 case 'T':
391                     if( p_input )
392                     {
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) );
397                     }
398                     else
399                     {
400                         sprintf( buf, "--:--:--" );
401                     }
402                     INSERT_STRING( 1, buf );
403                     break;
404                 case '_':
405                     *d = '\n';
406                     d++;
407                     break;
408
409                 default:
410                     *d = *s;
411                     d++;
412                     break;
413             }
414             b_is_format = 0;
415         }
416         else if( *s == '$' )
417         {
418             b_is_format = 1;
419         }
420         else
421         {
422             *d = *s;
423             d++;
424         }
425         s++;
426     }
427     *d = '\0';
428
429     if( p_input )
430     {
431         vlc_object_release( p_input );
432         if( p_item )
433             vlc_mutex_unlock( &p_item->lock );
434     }
435
436     return dst;
437 }
438
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 )
445 {
446     filter_sys_t *p_sys = p_filter->p_sys;
447     subpicture_t *p_spu;
448     video_format_t fmt;
449     time_t t;
450     char *buf;
451
452     if( p_sys->last_time == time( NULL ) )
453     {
454         return NULL;
455     }
456
457 /*    if( p_sys->b_need_update == VLC_FALSE )
458     {
459         return NULL;
460     }*/
461
462     p_spu = p_filter->pf_sub_buffer_new( p_filter );
463     if( !p_spu ) return NULL;
464
465     memset( &fmt, 0, sizeof(video_format_t) );
466     fmt.i_chroma = VLC_FOURCC('T','E','X','T');
467     fmt.i_aspect = 0;
468     fmt.i_width = fmt.i_height = 0;
469     fmt.i_x_offset = 0;
470     fmt.i_y_offset = 0;
471     p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
472     if( !p_spu->p_region )
473     {
474         p_filter->pf_sub_buffer_del( p_filter, p_spu );
475         return NULL;
476     }
477
478     t = p_sys->last_time = time( NULL );
479
480     buf = FormatTime( p_sys->psz_marquee );
481     p_spu->p_region->psz_text = FormatMeta( VLC_OBJECT( p_filter ), buf );
482     free( 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;
486
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;
491         p_spu->i_x = 0;
492         p_spu->i_y = 0;
493         p_spu->b_absolute = VLC_FALSE;
494     }
495     else
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;
501     }
502     p_spu->p_region->p_style = p_sys->p_style;
503
504     p_sys->b_need_update = VLC_FALSE;
505     return p_spu;
506 }
507
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,
513                             void *p_data )
514 {
515     filter_sys_t *p_sys = (filter_sys_t *) p_data;
516
517     if( !strncmp( psz_var, "marq-marquee", 7 ) )
518     {
519         if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
520         p_sys->psz_marquee = strdup( newval.psz_string );
521     }
522     else if ( !strncmp( psz_var, "marq-x", 6 ) )
523     {
524         p_sys->i_xoff = newval.i_int;
525     }
526     else if ( !strncmp( psz_var, "marq-y", 6 ) )
527     {
528         p_sys->i_yoff = newval.i_int;
529     }
530     else if ( !strncmp( psz_var, "marq-color", 8 ) )  /* "marq-col" */
531     {
532         p_sys->p_style->i_font_color = newval.i_int;
533     }
534     else if ( !strncmp( psz_var, "marq-opacity", 8 ) ) /* "marq-opa" */
535     {
536         p_sys->p_style->i_font_alpha = 255 - newval.i_int;
537     }
538     else if ( !strncmp( psz_var, "marq-size", 6 ) )
539     {
540         p_sys->p_style->i_font_size = newval.i_int;
541     }
542     else if ( !strncmp( psz_var, "marq-timeout", 12 ) )
543     {
544         p_sys->i_timeout = newval.i_int;
545     }
546     else if ( !strncmp( psz_var, "marq-position", 8 ) )
547     /* willing to accept a match against marq-pos */
548     {
549         p_sys->i_pos = newval.i_int;
550         p_sys->i_xoff = -1;       /* force to relative positioning */
551     }
552     p_sys->b_need_update = VLC_TRUE;
553     return VLC_SUCCESS;
554 }