1 /*****************************************************************************
2 * osdmenu.c: osd filter module
3 *****************************************************************************
4 * Copyright (C) 2004-2005 M2X
5 * $Id: osdmenu.c 11131 2005-05-23 11:04:07Z hartman $
7 * Authors: Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implid warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
30 #include <vlc/input.h>
32 #include <vlc_filter.h>
33 #include <vlc_video.h>
38 /*****************************************************************************
40 *****************************************************************************/
42 /* FIXME: Future extension make the definition file in XML format. */
43 #define OSD_FILE_TEXT N_("OSD menu configuration file")
44 #define OSD_FILE_LONGTEXT N_( \
45 "An OSD menu configuration file that menu actions with button images" )
47 #define OSD_PATH_TEXT N_("Path to OSD menu images")
48 #define OSD_PATH_LONGTEXT N_( \
49 "Specify another path to the OSD menu images. This will override the path as defined in the " \
50 "OSD configuration file." )
52 #define POSX_TEXT N_("X coordinate of the OSD menu")
53 #define POSX_LONGTEXT N_("You can move the OSD menu by left-clicking on it." )
55 #define POSY_TEXT N_("Y coordinate of the OSD menu")
56 #define POSY_LONGTEXT N_("You can move the OSD menu by left-clicking on it." )
58 #define POS_TEXT N_("OSD menu position")
59 #define POS_LONGTEXT N_( \
60 "You can enforce the OSD menu position on the video " \
61 "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
62 "also use combinations of these values).")
64 #define TIMEOUT_TEXT N_("Timeout of OSD menu")
65 #define TIMEOUT_LONGTEXT N_( \
66 "OSD menu pictures get a default timeout of 15 seconds added to their remaining time." \
67 "This will ensure that they are at least the specified time visible.")
69 #define OSD_REPEAT_TEXT N_("Repeat the same OSD menu pictures n-times (default is 2)")
70 #define OSD_REPEAT_LONGTEXT N_( \
71 "Resend the same OSD menu picture n-times. In an environment where " \
72 "transmissions errors occur having a little redundancy is helpfull." )
74 #define OSD_REPEAT_DELAY_TEXT N_("Delay the subsequent OSD menu pictures by n ms (default is 200ms) ")
75 #define OSD_REPEAT_DELAY_LONGTEXT N_( \
76 "Delay the resending of the OSD menu picture for at least n-ms." )
78 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
79 static char *ppsz_pos_descriptions[] =
80 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
81 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
83 /* subfilter functions */
84 static int CreateFilter ( vlc_object_t * );
85 static void DestroyFilter( vlc_object_t * );
86 static subpicture_t *Filter( filter_t *, mtime_t );
87 static int OSDMenuUpdateEvent( vlc_object_t *, char const *,
88 vlc_value_t, vlc_value_t, void * );
89 static int OSDMenuVisibleEvent( vlc_object_t *, char const *,
90 vlc_value_t, vlc_value_t, void * );
92 #define OSD_CFG "osdmenu-"
94 #if defined( WIN32 ) || defined( UNDER_CE )
95 #define OSD_DEFAULT_CFG "osdmenu/default.cfg"
97 #define OSD_DEFAULT_CFG "share/osdmenu/default.cfg"
101 add_integer( OSD_CFG "x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_FALSE );
102 add_integer( OSD_CFG "y", -1, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_FALSE );
103 add_integer( OSD_CFG "position", 8, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
104 change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
105 add_string( OSD_CFG "file", OSD_DEFAULT_CFG, NULL, OSD_FILE_TEXT,
106 OSD_FILE_LONGTEXT, VLC_FALSE );
107 add_string( OSD_CFG "file-path", NULL, NULL, OSD_PATH_TEXT,
108 OSD_PATH_LONGTEXT, VLC_FALSE );
109 add_integer( OSD_CFG "timeout", 0, NULL, TIMEOUT_TEXT,
110 TIMEOUT_LONGTEXT, VLC_FALSE );
111 add_integer( OSD_CFG "repeat", 0, NULL, OSD_REPEAT_TEXT,
112 OSD_REPEAT_LONGTEXT, VLC_TRUE );
113 add_integer( OSD_CFG "repeat-delay", 200, NULL, OSD_REPEAT_DELAY_TEXT,
114 OSD_REPEAT_DELAY_LONGTEXT, VLC_TRUE );
116 set_capability( "sub filter", 100 );
117 set_description( N_("On Screen Display menu subfilter") );
118 set_shortname( N_("OSD menu") );
119 add_shortcut( "osdmenu" );
120 set_category( CAT_VIDEO );
121 set_subcategory( SUBCAT_VIDEO_SUBPIC );
122 set_callbacks( CreateFilter, DestroyFilter );
125 /*****************************************************************************
127 *****************************************************************************/
129 /*****************************************************************************
131 *****************************************************************************/
136 int position; /* relative positioning of SPU images */
137 mtime_t i_last_date; /* last mdate SPU object has been sent to SPU subsytem */
138 int i_timeout; /* duration SPU object is valid on the video output in seconds */
140 vlc_bool_t b_absolute; /* do we use absolute positioning or relative? */
141 vlc_bool_t b_update; /* Update OSD Menu by sending SPU objects */
142 vlc_bool_t b_visible; /* OSD Menu is visible */
143 int i_repeat_delay;/* Delay OSD menu repeat by n-ms */
144 int i_repeat; /* default value to use for OSD menu repeat */
145 int i_spu_repeat; /* repeat OSD menu */
147 char *psz_file; /* OSD Menu configuration file */
148 osd_menu_t *p_menu; /* pointer to OSD Menu object */
151 /*****************************************************************************
152 * CreateFilter: Create the filter and open the definition file
153 *****************************************************************************/
154 static int CreateFilter ( vlc_object_t *p_this )
156 filter_t *p_filter = (filter_t *)p_this;
160 p_filter->p_sys = (filter_sys_t *) malloc( sizeof( filter_sys_t ) );
161 if( !p_filter->p_sys )
163 msg_Err( p_filter, "out of memory" );
167 /* Populating struct */
168 p_filter->p_sys->p_menu = NULL;
169 p_filter->p_sys->psz_file = NULL;
171 vlc_mutex_init( p_filter, &p_filter->p_sys->lock );
173 p_filter->p_sys->psz_file = config_GetPsz( p_filter, OSD_CFG "file" );
174 if( p_filter->p_sys->psz_file == NULL || *p_filter->p_sys->psz_file == '\0' )
176 msg_Err( p_filter, "unable to get filename" );
180 var_Create( p_this, OSD_CFG "position", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
181 var_Get( p_this, OSD_CFG "position", &val );
182 p_filter->p_sys->position = val.i_int;
183 var_Create( p_this, OSD_CFG "x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
184 var_Get( p_this, OSD_CFG "x", &val );
186 var_Create( p_this, OSD_CFG "y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
187 var_Get( p_this, OSD_CFG "y", &val );
189 var_Create( p_this, OSD_CFG "timeout", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
190 var_Get( p_this, OSD_CFG "timeout", &val );
191 p_filter->p_sys->i_timeout = val.i_int; /* in seconds */
193 /* repeat the OSD menu n-times */
194 var_Create( p_this, OSD_CFG "repeat", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
195 var_Get( p_this, OSD_CFG "repeat", &val );
196 p_filter->p_sys->i_repeat = val.i_int;
197 var_Create( p_this, OSD_CFG "repeat-delay", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
198 var_Get( p_this, OSD_CFG "repeat-delay", &val );
199 p_filter->p_sys->i_repeat_delay = val.i_int;
201 /* Load the osd menu subsystem */
202 p_filter->p_sys->p_menu = osd_MenuCreate( p_this, p_filter->p_sys->psz_file );
203 if( p_filter->p_sys->p_menu == NULL )
206 /* Check if menu position was overridden */
207 p_filter->p_sys->b_absolute = VLC_TRUE;
209 if( posx < 0 || posy < 0)
211 p_filter->p_sys->b_absolute = VLC_FALSE;
212 p_filter->p_sys->p_menu->i_x = 0;
213 p_filter->p_sys->p_menu->i_y = 0;
215 else if( posx >= 0 || posy >= 0 )
217 p_filter->p_sys->p_menu->i_x = posx;
218 p_filter->p_sys->p_menu->i_y = posy;
220 else if( p_filter->p_sys->p_menu->i_x < 0 || p_filter->p_sys->p_menu->i_y < 0 )
222 p_filter->p_sys->b_absolute = VLC_FALSE;
223 p_filter->p_sys->p_menu->i_x = 0;
224 p_filter->p_sys->p_menu->i_y = 0;
227 /* Set up p_filter */
228 p_filter->p_sys->i_last_date = mdate();
230 /* Keep track of OSD Events */
231 p_filter->p_sys->b_update = VLC_FALSE;
232 p_filter->p_sys->b_visible = VLC_FALSE;
234 var_AddCallback( p_filter->p_sys->p_menu, "osd-menu-update", OSDMenuUpdateEvent, p_filter );
235 var_AddCallback( p_filter->p_sys->p_menu, "osd-menu-visible", OSDMenuVisibleEvent, p_filter );
237 /* Attach subpicture filter callback */
238 p_filter->pf_sub_filter = Filter;
240 es_format_Init( &p_filter->fmt_out, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
241 p_filter->fmt_out.i_priority = 0;
243 msg_Dbg( p_filter, "successfully loaded osdmenu filter" );
247 msg_Err( p_filter, "osdmenu filter discarded" );
248 vlc_mutex_destroy( &p_filter->p_sys->lock );
249 if( p_filter->p_sys->p_menu )
251 osd_MenuDelete( p_this, p_filter->p_sys->p_menu );
252 p_filter->p_sys->p_menu = NULL;
254 if( p_filter->p_sys->psz_file ) free( p_filter->p_sys->psz_file );
255 if( p_filter->p_sys ) free( p_filter->p_sys );
259 /*****************************************************************************
260 * DestroyFilter: Make a clean exit of this plugin
261 *****************************************************************************/
262 static void DestroyFilter( vlc_object_t *p_this )
264 filter_t *p_filter = (filter_t*)p_this;
265 filter_sys_t *p_sys = p_filter->p_sys;
267 var_Destroy( p_this, OSD_CFG "file" );
268 var_Destroy( p_this, OSD_CFG "x" );
269 var_Destroy( p_this, OSD_CFG "y" );
270 var_Destroy( p_this, OSD_CFG "position" );
271 var_Destroy( p_this, OSD_CFG "timeout" );
272 var_Destroy( p_this, OSD_CFG "repeat" );
273 var_Destroy( p_this, OSD_CFG "repeat-delay" );
275 var_DelCallback( p_sys->p_menu, "osd-menu-update", OSDMenuUpdateEvent, p_filter );
276 var_DelCallback( p_sys->p_menu, "osd-menu-visible", OSDMenuVisibleEvent, p_filter );
278 osd_MenuDelete( p_filter, p_sys->p_menu );
280 vlc_mutex_destroy( &p_filter->p_sys->lock );
281 if( p_sys->psz_file) free( p_sys->psz_file );
282 if( p_sys ) free( p_sys );
284 msg_Dbg( p_filter, "osdmenu filter destroyed" );
287 /*****************************************************************************
288 * OSDMenuEvent: callback for OSD Menu events
289 *****************************************************************************/
290 static int OSDMenuVisibleEvent( vlc_object_t *p_this, char const *psz_var,
291 vlc_value_t oldval, vlc_value_t newval, void *p_data )
293 filter_t *p_filter = (filter_t *) p_data;
295 p_filter->p_sys->b_visible = VLC_TRUE;
299 static int OSDMenuUpdateEvent( vlc_object_t *p_this, char const *psz_var,
300 vlc_value_t oldval, vlc_value_t newval, void *p_data )
302 filter_t *p_filter = (filter_t *) p_data;
304 p_filter->p_sys->b_update = VLC_TRUE;
305 p_filter->p_sys->i_spu_repeat = p_filter->p_sys->i_repeat;
310 /*****************************************************************************
311 * create_text_region : compose a text region SPU
312 *****************************************************************************/
313 static subpicture_region_t *create_text_region( filter_t *p_filter, subpicture_t *p_spu,
314 int i_width, int i_height, const char *psz_text )
316 subpicture_region_t *p_region;
319 /* Create new SPU region */
320 memset( &fmt, 0, sizeof(video_format_t) );
321 fmt.i_chroma = VLC_FOURCC( 'T','E','X','T' );
322 fmt.i_aspect = VOUT_ASPECT_FACTOR;
323 fmt.i_sar_num = fmt.i_sar_den = 1;
324 fmt.i_width = fmt.i_visible_width = i_width;
325 fmt.i_height = fmt.i_visible_height = i_height;
326 fmt.i_x_offset = fmt.i_y_offset = 0;
327 p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
330 msg_Err( p_filter, "cannot allocate another SPU region" );
333 p_region->psz_text = strdup( psz_text );
337 msg_Dbg( p_filter, "SPU text region position (%d,%d) (%d,%d) [%s]",
338 p_region->i_x, p_region->i_y,
339 p_region->fmt.i_width, p_region->fmt.i_height, p_region->psz_text );
345 /*****************************************************************************
346 * create_picture_region : compose a picture region SPU
347 *****************************************************************************/
348 static subpicture_region_t *create_picture_region( filter_t *p_filter, subpicture_t *p_spu,
349 int i_width, int i_height, picture_t *p_pic )
351 subpicture_region_t *p_region;
354 if( !p_spu ) return NULL;
356 /* Create new SPU region */
357 memset( &fmt, 0, sizeof(video_format_t) );
358 fmt.i_chroma = (p_pic == NULL) ? VLC_FOURCC('Y','U','V','P') : VLC_FOURCC('Y','U','V','A');
359 fmt.i_aspect = VOUT_ASPECT_FACTOR;
360 fmt.i_sar_num = fmt.i_sar_den = 1;
361 fmt.i_width = fmt.i_visible_width = i_width;
362 fmt.i_height = fmt.i_visible_height = i_height;
363 fmt.i_x_offset = fmt.i_y_offset = 0;
364 p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
367 msg_Err( p_filter, "cannot allocate SPU region" );
368 p_filter->pf_sub_buffer_del( p_filter, p_spu );
371 if( !p_pic && ( fmt.i_chroma == VLC_FOURCC('Y','U','V','P') ) )
373 p_region->fmt.p_palette->i_entries = 0;
374 p_region->fmt.i_width = p_region->fmt.i_visible_width = 0;
375 p_region->fmt.i_height = p_region->fmt.i_visible_height = 0;
378 vout_CopyPicture( p_filter, &p_region->picture, p_pic );
383 msg_Dbg( p_filter, "SPU picture region position (%d,%d) (%d,%d) [%p]",
384 p_region->i_x, p_region->i_y,
385 p_region->fmt.i_width, p_region->fmt.i_height, p_pic );
390 /****************************************************************************
391 * Filter: the whole thing
392 ****************************************************************************
393 * This function outputs subpictures at regular time intervals.
394 ****************************************************************************/
395 static subpicture_t *Filter( filter_t *p_filter, mtime_t i_date )
397 filter_sys_t *p_sys = p_filter->p_sys;
399 subpicture_region_t *p_region;
401 if( !p_filter->p_sys->b_update )
404 /* use a window of 200ms by default */
405 if( ( p_filter->p_sys->i_spu_repeat < p_filter->p_sys->i_repeat ) &&
406 ( p_filter->p_sys->i_last_date +
407 (mtime_t)( p_filter->p_sys->i_repeat_delay * 1000 ) < i_date ) )
410 msg_Dbg( p_filter, " waiting a bit %d", p_filter->p_sys->i_spu_repeat );
414 p_filter->p_sys->i_last_date = i_date;
416 /* repeat the OSD menu ? */
417 p_filter->p_sys->i_spu_repeat--;
418 if( p_filter->p_sys->i_spu_repeat <= 0 )
419 p_filter->p_sys->b_update = VLC_FALSE;
421 msg_Dbg( p_filter, "i_spu_repeat %d, i_repeat %d, i_date "I64Fd,
422 p_filter->p_sys->i_spu_repeat, p_filter->p_sys->i_repeat, i_date );
425 /* Allocate the subpicture internal data. */
426 p_spu = p_filter->pf_sub_buffer_new( p_filter );
427 if( !p_spu ) return NULL;
429 p_spu->b_absolute = p_sys->b_absolute;
430 p_spu->i_start = p_sys->i_last_date = i_date;
431 p_spu->i_stop = (p_sys->i_timeout == 0) ? 0 : i_date + (mtime_t)(p_sys->i_timeout * 1000000);
432 p_spu->b_ephemer = VLC_TRUE;
433 p_spu->b_fade = VLC_TRUE;
434 p_spu->i_flags = p_sys->position;
436 /* Send an empty subpicture to clear the display
437 * when OSD menu should be hidden and menu picture is not allocated.
439 if( !p_filter->p_sys->p_menu->p_state->p_pic ||
440 ( p_filter->p_sys->b_visible == VLC_FALSE ) )
442 /* Create new spu regions and allocate an empty picture in it. */
443 p_region = create_picture_region( p_filter, p_spu,
444 p_filter->p_sys->p_menu->p_state->i_width,
445 p_filter->p_sys->p_menu->p_state->i_height,
448 /* proper positioning of OSD menu image */
449 p_spu->i_x = p_filter->p_sys->p_menu->p_state->i_x;
450 p_spu->i_y = p_filter->p_sys->p_menu->p_state->i_y;
451 p_spu->p_region = p_region;
452 p_spu->i_alpha = 0xFF; /* Picture is completely transparent. */
456 /* Create new spu regions */
457 p_region = create_picture_region( p_filter, p_spu,
458 p_filter->p_sys->p_menu->p_state->i_width,
459 p_filter->p_sys->p_menu->p_state->i_height,
460 p_filter->p_sys->p_menu->p_state->p_pic );
462 p_region->p_next = create_text_region( p_filter, p_spu,
463 p_filter->p_sys->p_menu->p_state->i_width, p_filter->p_sys->p_menu->p_state->i_height,
464 p_filter->p_sys->p_menu->p_state->p_visible->psz_action );
467 /* proper positioning of OSD menu image */
468 p_spu->i_x = p_filter->p_sys->p_menu->p_state->i_x;
469 p_spu->i_y = p_filter->p_sys->p_menu->p_state->i_y;
470 p_spu->p_region = p_region;