3f3efd7a32d14498ba74c85530b910f2c7cc6d7c
[vlc.git] / modules / video_filter / marq.c
1 /*****************************************************************************
2  * marq.c : marquee display video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2008 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
30 #include <vlc/vlc.h>
31 #include <vlc_vout.h>
32
33 #include "vlc_filter.h"
34 #include "vlc_block.h"
35 #include "vlc_osd.h"
36
37 #include "vlc_strings.h"
38
39 /*****************************************************************************
40  * Local prototypes
41  *****************************************************************************/
42 static int  CreateFilter ( vlc_object_t * );
43 static void DestroyFilter( vlc_object_t * );
44 static subpicture_t *Filter( filter_t *, mtime_t );
45
46
47 static int MarqueeCallback( vlc_object_t *p_this, char const *psz_var,
48                             vlc_value_t oldval, vlc_value_t newval,
49                             void *p_data );
50 static int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
51                0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
52                0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
53                0x00000080, 0x000000FF, 0x0000FFFF};
54 static const char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
55                N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
56                N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
57                N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
58                N_("Aqua") };
59
60 /*****************************************************************************
61  * filter_sys_t: marquee filter descriptor
62  *****************************************************************************/
63 struct filter_sys_t
64 {
65     int i_xoff, i_yoff;  /* offsets for the display string in the video window */
66     int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
67     int i_timeout;
68
69     char *psz_marquee;    /* marquee string */
70
71     text_style_t *p_style; /* font control */
72
73     time_t last_time;
74
75     vlc_bool_t b_need_update;
76 };
77
78 #define MSG_TEXT N_("Text")
79 #define MSG_LONGTEXT N_( \
80     "Marquee text to display. " \
81     "(Available format strings: " \
82     "Time related: %Y = year, %m = month, %d = day, %H = hour, " \
83     "%M = minute, %S = second, ... " \
84     "Meta data related: $a = artist, $b = album, $c = copyright, " \
85     "$d = description, $e = encoded by, $g = genre, " \
86     "$l = language, $n = track num, $p = now playing, " \
87     "$r = rating, $s = subtitles language, $t = title, "\
88     "$u = url, $A = date, " \
89     "$B = audio bitrate (in kb/s), $C = chapter," \
90     "$D = duration, $F = full name with path, $I = title, "\
91     "$L = time left, " \
92     "$N = name, $O = audio language, $P = position (in %), $R = rate, " \
93     "$S = audio sample rate (in kHz), " \
94     "$T = time, $U = publisher, $V = volume, $_ = 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 const 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 #define CFG_PREFIX "marq-"
128
129 /*****************************************************************************
130  * Module descriptor
131  *****************************************************************************/
132 vlc_module_begin();
133     set_capability( "sub filter", 0 );
134     set_shortname( _("Marquee" ));
135     set_callbacks( CreateFilter, DestroyFilter );
136     set_category( CAT_VIDEO );
137     set_subcategory( SUBCAT_VIDEO_SUBPIC );
138     add_string( CFG_PREFIX "marquee", "VLC", NULL, MSG_TEXT, MSG_LONGTEXT,
139                 VLC_FALSE );
140
141     set_section( N_("Position"), NULL );
142     add_integer( CFG_PREFIX "x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
143     add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
144     add_integer( CFG_PREFIX "position", -1, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
145         change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
146
147     set_section( N_("Font"), NULL );
148     /* 5 sets the default to top [1] left [4] */
149     add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
150         OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
151     add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
152                  VLC_FALSE );
153         change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
154     add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT,
155                  VLC_FALSE );
156
157     set_section( N_("Misc"), NULL );
158     add_integer( CFG_PREFIX "timeout", 0, NULL, TIMEOUT_TEXT, TIMEOUT_LONGTEXT,
159                  VLC_FALSE );
160
161     set_description( _("Marquee display") );
162     add_shortcut( "time" );
163     add_obsolete_string( "time-format" );
164     add_obsolete_string( "time-x" );
165     add_obsolete_string( "time-y" );
166     add_obsolete_string( "time-position" );
167     add_obsolete_string( "time-opacity" );
168     add_obsolete_string( "time-color" );
169     add_obsolete_string( "time-size" );
170 vlc_module_end();
171
172 static const char *ppsz_filter_options[] = {
173     "marquee", "x", "y", "position", "color", "size", "timeout", NULL
174 };
175
176 /*****************************************************************************
177  * CreateFilter: allocates marquee video filter
178  *****************************************************************************/
179 static int CreateFilter( vlc_object_t *p_this )
180 {
181     filter_t *p_filter = (filter_t *)p_this;
182     filter_sys_t *p_sys;
183
184     /* Allocate structure */
185     p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
186     if( p_sys == NULL )
187     {
188         msg_Err( p_filter, "out of memory" );
189         return VLC_ENOMEM;
190     }
191
192     p_sys->p_style = malloc( sizeof( text_style_t ) );
193     memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ) );
194
195     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
196                        p_filter->p_cfg );
197
198 #define CREATE_VAR( stor, type, var ) \
199     p_sys->stor = var_CreateGet##type##Command( p_filter, var ); \
200     var_AddCallback( p_filter, var, MarqueeCallback, p_sys );
201
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" );
210
211     p_sys->p_style->i_font_alpha = 255 - p_sys->p_style->i_font_alpha ;
212
213     /* Misc init */
214     p_filter->pf_sub_filter = Filter;
215     p_sys->last_time = ((time_t)-1);
216     p_sys->b_need_update = VLC_TRUE;
217
218     return VLC_SUCCESS;
219 }
220 /*****************************************************************************
221  * DestroyFilter: destroy marquee video filter
222  *****************************************************************************/
223 static void DestroyFilter( vlc_object_t *p_this )
224 {
225     filter_t *p_filter = (filter_t *)p_this;
226     filter_sys_t *p_sys = p_filter->p_sys;
227
228     if( p_sys->p_style ) free( p_sys->p_style );
229     if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
230
231     /* Delete the marquee variables */
232 #define DEL_VAR(var) \
233     var_DelCallback( p_filter, var, MarqueeCallback, p_sys ); \
234     var_Destroy( p_filter, var );
235     DEL_VAR( "marq-x" );
236     DEL_VAR( "marq-y" );
237     DEL_VAR( "marq-marquee" );
238     DEL_VAR( "marq-timeout" );
239     DEL_VAR( "marq-position" );
240     DEL_VAR( "marq-color" );
241     DEL_VAR( "marq-opacity" );
242     DEL_VAR( "marq-size" );
243
244     free( p_sys );
245 }
246
247 /****************************************************************************
248  * Filter: the whole thing
249  ****************************************************************************
250  * This function outputs subpictures at regular time intervals.
251  ****************************************************************************/
252 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
253 {
254     filter_sys_t *p_sys = p_filter->p_sys;
255     subpicture_t *p_spu;
256     video_format_t fmt;
257     time_t t;
258
259     if( p_sys->last_time == time( NULL ) )
260     {
261         return NULL;
262     }
263
264     if( p_sys->b_need_update == VLC_FALSE )
265     {
266         return NULL;
267     }
268
269     p_spu = p_filter->pf_sub_buffer_new( p_filter );
270     if( !p_spu ) return NULL;
271
272     memset( &fmt, 0, sizeof(video_format_t) );
273     fmt.i_chroma = VLC_FOURCC('T','E','X','T');
274     fmt.i_aspect = 0;
275     fmt.i_width = fmt.i_height = 0;
276     fmt.i_x_offset = 0;
277     fmt.i_y_offset = 0;
278     p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
279     if( !p_spu->p_region )
280     {
281         p_filter->pf_sub_buffer_del( p_filter, p_spu );
282         return NULL;
283     }
284
285     t = p_sys->last_time = time( NULL );
286
287     if( strchr( p_sys->psz_marquee, '%' ) || strchr( p_sys->psz_marquee, '$' ) )
288     {
289         p_sys->b_need_update = VLC_TRUE;
290     }
291     else
292     {
293         p_sys->b_need_update = VLC_FALSE;
294     }
295     p_spu->p_region->psz_text = str_format( p_filter, p_sys->psz_marquee );
296     p_spu->i_start = date;
297     p_spu->i_stop  = p_sys->i_timeout == 0 ? 0 : date + p_sys->i_timeout * 1000;
298     p_spu->b_ephemer = VLC_TRUE;
299
300     /*  where to locate the string: */
301     if( p_sys->i_pos < 0 )
302     {   /* set to one of the 9 relative locations */
303         p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
304         p_spu->b_absolute = VLC_TRUE;
305     }
306     else
307     {   /*  set to an absolute xy */
308         p_spu->p_region->i_align = p_sys->i_pos;
309         p_spu->b_absolute = VLC_FALSE;
310     }
311
312     p_spu->i_x = p_sys->i_xoff;
313     p_spu->i_y = p_sys->i_yoff;
314
315     p_spu->p_region->p_style = p_sys->p_style;
316
317     return p_spu;
318 }
319
320 /**********************************************************************
321  * Callback to update params on the fly
322  **********************************************************************/
323 static int MarqueeCallback( vlc_object_t *p_this, char const *psz_var,
324                             vlc_value_t oldval, vlc_value_t newval,
325                             void *p_data )
326 {
327     filter_sys_t *p_sys = (filter_sys_t *) p_data;
328
329     if( !strncmp( psz_var, "marq-marquee", 7 ) )
330     {
331         if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
332         p_sys->psz_marquee = strdup( newval.psz_string );
333     }
334     else if ( !strncmp( psz_var, "marq-x", 6 ) )
335     {
336         p_sys->i_xoff = newval.i_int;
337     }
338     else if ( !strncmp( psz_var, "marq-y", 6 ) )
339     {
340         p_sys->i_yoff = newval.i_int;
341     }
342     else if ( !strncmp( psz_var, "marq-color", 8 ) )  /* "marq-col" */
343     {
344         p_sys->p_style->i_font_color = newval.i_int;
345     }
346     else if ( !strncmp( psz_var, "marq-opacity", 8 ) ) /* "marq-opa" */
347     {
348         p_sys->p_style->i_font_alpha = 255 - newval.i_int;
349     }
350     else if ( !strncmp( psz_var, "marq-size", 6 ) )
351     {
352         p_sys->p_style->i_font_size = newval.i_int;
353     }
354     else if ( !strncmp( psz_var, "marq-timeout", 12 ) )
355     {
356         p_sys->i_timeout = newval.i_int;
357     }
358     else if ( !strncmp( psz_var, "marq-position", 8 ) )
359     /* willing to accept a match against marq-pos */
360     {
361         p_sys->i_pos = newval.i_int;
362         p_sys->i_xoff = -1;       /* force to relative positioning */
363     }
364     p_sys->b_need_update = VLC_TRUE;
365     return VLC_SUCCESS;
366 }