1 /*****************************************************************************
2 * fbosd.c : framebuffer osd plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2007-2008, the VideoLAN team
7 * Authors: Jean-Paul Saman
8 * Copied from modules/video_output/fb.c by Samuel Hocevar <sam@zoy.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
33 #include <vlc_plugin.h>
36 #include <stdlib.h> /* free() */
37 #include <string.h> /* strerror() */
38 #include <fcntl.h> /* open() */
39 #include <unistd.h> /* close() */
41 #include <sys/ioctl.h>
42 #include <sys/mman.h> /* mmap() */
46 #include <vlc_image.h>
47 #include <vlc_interface.h>
48 #include <vlc_input.h>
50 #include <vlc_filter.h>
52 #include <vlc_strings.h>
57 /*****************************************************************************
59 *****************************************************************************/
60 static int Create ( vlc_object_t * );
61 static void Destroy ( vlc_object_t * );
62 static void Run ( intf_thread_t * );
64 static int Init ( intf_thread_t * );
65 static void End ( intf_thread_t * );
67 static int OpenDisplay ( intf_thread_t * );
68 static void CloseDisplay ( intf_thread_t * );
70 /* Load modules needed for rendering and blending */
71 #if defined(FBOSD_BLENDING)
72 static int OpenBlending ( intf_thread_t * );
73 static void CloseBlending ( intf_thread_t * );
75 static int OpenTextRenderer ( intf_thread_t * );
76 static void CloseTextRenderer( intf_thread_t * );
78 /* Manipulate the overlay buffer */
79 static int OverlayCallback( vlc_object_t *, char const *,
80 vlc_value_t, vlc_value_t, void * );
82 static picture_t *AllocatePicture( vlc_object_t *,
84 static void DeAllocatePicture( vlc_object_t *, picture_t *,
86 static void SetOverlayTransparency( intf_thread_t *,
88 static picture_t *LoadImage( intf_thread_t *, video_format_t *,
91 #if defined(FBOSD_BLENDING)
92 static int BlendPicture( intf_thread_t *, video_format_t *,
93 video_format_t *, picture_t *, picture_t * );
95 static picture_t *ConvertImage( intf_thread_t *, picture_t *,
96 video_format_t *, video_format_t * );
98 static int RenderPicture( intf_thread_t *, int, int,
99 picture_t *, picture_t * );
100 static picture_t *RenderText( intf_thread_t *, const char *,
101 text_style_t *, video_format_t * );
103 #define DEVICE_TEXT N_("Framebuffer device")
104 #define DEVICE_LONGTEXT N_( \
105 "Framebuffer device to use for rendering (usually /dev/fb0).")
107 #define ASPECT_RATIO_TEXT N_("Video aspect ratio")
108 #define ASPECT_RATIO_LONGTEXT N_( \
109 "Aspect ratio of the video image (4:3, 16:9). Default is square pixels." )
111 #define FBOSD_IMAGE_TEXT N_("Image file")
112 #define FBOSD_IMAGE_LONGTEXT N_( \
113 "Filename of image file to use on the overlay framebuffer." )
115 #define ALPHA_TEXT N_("Transparency of the image")
116 #define ALPHA_LONGTEXT N_( "Transparency value of the new image " \
117 "used in blending. By default it set to fully opaque (255). " \
118 "(from 0 for full transparency to 255 for full opacity)" )
120 #define FBOSD_TEXT N_("Text")
121 #define FBOSD_LONGTEXT N_( "Text to display on the overlay framebuffer." )
123 #define POSX_TEXT N_("X coordinate")
124 #define POSX_LONGTEXT N_("X coordinate of the rendered image")
126 #define POSY_TEXT N_("Y coordinate")
127 #define POSY_LONGTEXT N_("Y coordinate of the rendered image")
129 #define POS_TEXT N_("Position")
130 #define POS_LONGTEXT N_( \
131 "You can enforce the picture position on the overlay " \
132 "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
133 "also use combinations of these values, e.g. 6=top-right).")
135 #define OPACITY_TEXT N_("Opacity")
136 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
137 "overlayed text. 0 = transparent, 255 = totally opaque. " )
139 #define SIZE_TEXT N_("Font size, pixels")
140 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
143 #define COLOR_TEXT N_("Color")
144 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
145 "the video. This must be an hexadecimal (like HTML colors). The first two "\
146 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
147 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
149 #define CLEAR_TEXT N_( "Clear overlay framebuffer" )
150 #define CLEAR_LONGTEXT N_( "The displayed overlay images is cleared by " \
151 "making the overlay completely transparent. All previously rendered " \
152 "images and text will be cleared from the cache." )
154 #define RENDER_TEXT N_( "Render text or image" )
155 #define RENDER_LONGTEXT N_( "Render the image or text in current overlay " \
158 #define DISPLAY_TEXT N_( "Display on overlay framebuffer" )
159 #define DISPLAY_LONGTEXT N_( "All rendered images and text will be " \
160 "displayed on the overlay framebuffer." )
162 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
163 static const char *ppsz_pos_descriptions[] =
164 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
165 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
167 static int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
168 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
169 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
170 0x00000080, 0x000000FF, 0x0000FFFF};
171 static const char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
172 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
173 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
174 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
178 set_shortname( "fbosd" );
179 set_category( CAT_INTERFACE );
180 set_subcategory( SUBCAT_INTERFACE_MAIN );
182 add_file( "fbosd-dev", "/dev/fb1", NULL, DEVICE_TEXT, DEVICE_LONGTEXT,
184 add_string( "fbosd-aspect-ratio", "", NULL, ASPECT_RATIO_TEXT,
185 ASPECT_RATIO_LONGTEXT, true );
187 add_string( "fbosd-image", NULL, NULL, FBOSD_IMAGE_TEXT,
188 FBOSD_IMAGE_LONGTEXT, true );
189 add_string( "fbosd-text", NULL, NULL, FBOSD_TEXT,
190 FBOSD_LONGTEXT, true );
192 #if defined(FBOSD_BLENDING)
193 add_integer_with_range( "fbosd-alpha", 255, 0, 255, NULL, ALPHA_TEXT,
194 ALPHA_LONGTEXT, true );
198 set_section( N_("Position"), NULL );
199 add_integer( "fbosd-x", 0, NULL, POSX_TEXT,
200 POSX_LONGTEXT, false );
201 add_integer( "fbosd-y", 0, NULL, POSY_TEXT,
202 POSY_LONGTEXT, false );
203 add_integer( "fbosd-position", 8, NULL, POS_TEXT, POS_LONGTEXT, true );
204 change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
206 set_section( N_("Font"), NULL );
207 add_integer_with_range( "fbosd-font-opacity", 255, 0, 255, NULL,
208 OPACITY_TEXT, OPACITY_LONGTEXT, false );
209 add_integer( "fbosd-font-color", 0x00FFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
211 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
212 add_integer( "fbosd-font-size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT,
215 set_section( N_("Commands"), NULL );
216 add_bool( "fbosd-clear", false, NULL, CLEAR_TEXT, CLEAR_LONGTEXT, true );
217 add_bool( "fbosd-render", false, NULL, RENDER_TEXT, RENDER_LONGTEXT, true );
218 add_bool( "fbosd-display", false, NULL, DISPLAY_TEXT, DISPLAY_LONGTEXT, true );
220 set_description( _("GNU/Linux osd/overlay framebuffer interface") );
221 set_capability( "interface", 10 );
222 set_callbacks( Create, Destroy );
225 /*****************************************************************************
226 * fbosd_render_t: render descriptor
227 *****************************************************************************/
228 struct fbosd_render_t
230 #define FBOSD_RENDER_IMAGE 0
231 #define FBOSD_RENDER_TEXT 1
234 #define FBOSD_STATE_FREE 0
235 #define FBOSD_STATE_RESERVED 1
236 #define FBOSD_STATE_RENDER 2
240 text_style_t text_style; /* font control */
248 int i_alpha; /* transparency for images */
250 #define FBOSD_RENDER_MAX 10
252 /*****************************************************************************
253 * intf_sys_t: interface framebuffer method descriptor
254 *****************************************************************************/
257 /* Framebuffer information */
258 int i_fd; /* device handle */
259 struct fb_var_screeninfo var_info; /* current mode information */
260 bool b_pan; /* does device supports panning ? */
261 struct fb_cmap fb_cmap; /* original colormap */
262 uint16_t *p_palette; /* original palette */
264 /* Overlay framebuffer format */
265 video_format_t fmt_out;
266 picture_t *p_overlay;
267 size_t i_page_size; /* page size */
271 int i_bytes_per_pixel;
273 /* Image and Picture rendering */
274 image_handler_t *p_image;
275 #if defined(FBOSD_BLENDING)
276 filter_t *p_blend; /* alpha blending module */
278 filter_t *p_text; /* text renderer module */
281 struct fbosd_render_t render[FBOSD_RENDER_MAX];
284 text_style_t *p_style; /* font control */
292 int i_alpha; /* transparency for images */
294 /* commands control */
295 bool b_need_update; /* update display with \overlay buffer */
296 bool b_clear; /* clear overlay buffer make it tranparent */
297 bool b_render; /* render an image or text in overlay buffer */
300 /*****************************************************************************
301 * Create: allocates FB interface thread output method
302 *****************************************************************************/
303 static int Create( vlc_object_t *p_this )
305 intf_thread_t *p_intf = (intf_thread_t *)p_this;
311 /* Allocate instance and initialize some members */
312 p_intf->p_sys = p_sys = malloc( sizeof( intf_sys_t ) );
315 memset( p_sys, 0, sizeof(intf_sys_t) );
317 p_sys->p_style = malloc( sizeof( text_style_t ) );
318 if( !p_sys->p_style )
320 free( p_intf->p_sys );
323 vlc_memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ) );
325 p_intf->pf_run = Run;
327 p_sys->p_image = image_HandlerCreate( p_this );
328 if( !p_sys->p_image )
330 free( p_intf->p_sys->p_style );
331 free( p_intf->p_sys );
335 #if defined(FBOSD_BLENDING)
336 p_sys->i_alpha = var_CreateGetIntegerCommand( p_intf, "fbosd-alpha" );
337 var_AddCallback( p_intf, "fbosd-alpha", OverlayCallback, NULL );
339 p_sys->i_alpha = 255;
341 p_sys->i_aspect = -1;
343 var_CreateGetNonEmptyString( p_intf, "fbosd-aspect-ratio" );
346 char *psz_parser = strchr( psz_aspect, ':' );
350 *psz_parser++ = '\0';
351 p_sys->i_aspect = ( atoi( psz_aspect )
352 * VOUT_ASPECT_FACTOR ) / atoi( psz_parser );
353 p_sys->fmt_out.i_aspect = p_sys->i_aspect;
355 msg_Dbg( p_intf, "using aspect ratio %d:%d",
356 atoi( psz_aspect ), atoi( psz_parser ) );
362 /* Use PAL by default */
363 p_sys->i_width = p_sys->fmt_out.i_width = 704;
364 p_sys->i_height = p_sys->fmt_out.i_height = 576;
366 psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-image" );
367 var_AddCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
368 if( psz_tmp && *psz_tmp )
370 p_sys->render[0].i_type = FBOSD_RENDER_IMAGE;
371 p_sys->render[0].i_state = FBOSD_STATE_RENDER;
372 p_sys->render[0].psz_string = strdup( psz_tmp );
376 psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-text" );
377 var_AddCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
378 if( psz_tmp && *psz_tmp )
380 p_sys->render[1].i_type = FBOSD_RENDER_TEXT;
381 p_sys->render[1].i_state = FBOSD_STATE_RENDER;
382 p_sys->render[1].psz_string = strdup( psz_tmp );
386 p_sys->i_pos = var_CreateGetIntegerCommand( p_intf, "fbosd-position" );
387 p_sys->i_x = var_CreateGetIntegerCommand( p_intf, "fbosd-x" );
388 p_sys->i_y = var_CreateGetIntegerCommand( p_intf, "fbosd-y" );
390 var_AddCallback( p_intf, "fbosd-position", OverlayCallback, NULL );
391 var_AddCallback( p_intf, "fbosd-x", OverlayCallback, NULL );
392 var_AddCallback( p_intf, "fbosd-y", OverlayCallback, NULL );
394 p_sys->p_style->i_font_size =
395 var_CreateGetIntegerCommand( p_intf, "fbosd-font-size" );
396 p_sys->p_style->i_font_color =
397 var_CreateGetIntegerCommand( p_intf, "fbosd-font-color" );
398 p_sys->p_style->i_font_alpha = 255 -
399 var_CreateGetIntegerCommand( p_intf, "fbosd-font-opacity" );
401 var_AddCallback( p_intf, "fbosd-font-color", OverlayCallback, NULL );
402 var_AddCallback( p_intf, "fbosd-font-size", OverlayCallback, NULL );
403 var_AddCallback( p_intf, "fbosd-font-opacity", OverlayCallback, NULL );
405 for( i = 0; i < FBOSD_RENDER_MAX; i++ )
407 vlc_memcpy( &p_sys->render[i].text_style, &default_text_style,
408 sizeof( text_style_t ) );
411 p_sys->b_clear = var_CreateGetBoolCommand( p_intf, "fbosd-clear" );
412 p_sys->b_render = var_CreateGetBoolCommand( p_intf, "fbosd-render" );
413 p_sys->b_need_update = var_CreateGetBoolCommand( p_intf, "fbosd-display" );
415 var_AddCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
416 var_AddCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
417 var_AddCallback( p_intf, "fbosd-display", OverlayCallback, NULL );
419 /* Check if picture position was overridden */
420 p_sys->b_absolute = true;
421 if( ( p_sys->i_x >= 0 ) && ( p_sys->i_y >= 0 ) )
423 p_sys->b_absolute = false;
424 p_sys->i_y = (p_sys->i_y < p_sys->i_height) ?
425 p_sys->i_y : p_sys->i_height;
426 p_sys->i_x = (p_sys->i_x < p_sys->i_width) ?
427 p_sys->i_x : p_sys->i_width;
430 p_sys->render[0].i_x = p_sys->render[1].i_x = p_sys->i_x;
431 p_sys->render[0].i_y = p_sys->render[1].i_y = p_sys->i_y;
432 p_sys->render[0].i_pos = p_sys->render[1].i_pos = p_sys->i_pos;
433 p_sys->render[0].i_alpha = p_sys->render[1].i_alpha = p_sys->i_alpha;
435 /* Initialize framebuffer */
436 if( OpenDisplay( p_intf ) )
438 Destroy( VLC_OBJECT(p_intf) );
444 #if defined(FBOSD_BLENDING)
445 /* Load the blending module */
446 if( OpenBlending( p_intf ) )
448 msg_Err( p_intf, "Unable to load image blending module" );
449 Destroy( VLC_OBJECT(p_intf) );
454 /* Load text renderer module */
455 if( OpenTextRenderer( p_intf ) )
457 msg_Err( p_intf, "Unable to load text rendering module" );
458 Destroy( VLC_OBJECT(p_intf) );
462 p_sys->b_render = true;
463 p_sys->b_need_update = true;
468 /*****************************************************************************
469 * Destroy: destroy FB interface thread output method
470 *****************************************************************************
471 * Terminate an output method created by Create
472 *****************************************************************************/
473 static void Destroy( vlc_object_t *p_this )
475 intf_thread_t *p_intf = (intf_thread_t *)p_this;
476 intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
479 p_sys->b_need_update = false;
480 p_sys->b_render = false;
481 p_sys->b_clear = false;
483 #if defined(FBOSD_BLENDING)
484 var_DelCallback( p_intf, "fbosd-alpha", OverlayCallback, NULL );
485 var_Destroy( p_intf, "fbosd-alpha" );
488 var_DelCallback( p_intf, "fbosd-x", OverlayCallback, NULL );
489 var_DelCallback( p_intf, "fbosd-y", OverlayCallback, NULL );
490 var_DelCallback( p_intf, "fbosd-position", OverlayCallback, NULL );
491 var_DelCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
492 var_DelCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
493 var_DelCallback( p_intf, "fbosd-font-size", OverlayCallback, NULL );
494 var_DelCallback( p_intf, "fbosd-font-color", OverlayCallback, NULL );
495 var_DelCallback( p_intf, "fbosd-font-opacity", OverlayCallback, NULL );
496 var_DelCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
497 var_DelCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
498 var_DelCallback( p_intf, "fbosd-display", OverlayCallback, NULL );
500 var_Destroy( p_intf, "fbosd-x" );
501 var_Destroy( p_intf, "fbosd-y" );
502 var_Destroy( p_intf, "fbosd-position" );
503 var_Destroy( p_intf, "fbosd-image" );
504 var_Destroy( p_intf, "fbosd-text" );
505 var_Destroy( p_intf, "fbosd-font-size" );
506 var_Destroy( p_intf, "fbosd-font-color" );
507 var_Destroy( p_intf, "fbosd-font-opacity" );
508 var_Destroy( p_intf, "fbosd-clear" );
509 var_Destroy( p_intf, "fbosd-render" );
510 var_Destroy( p_intf, "fbosd-display" );
512 var_Destroy( p_intf, "fbosd-aspect-ratio" );
514 CloseDisplay( p_intf );
516 for( i = 0; i < FBOSD_RENDER_MAX; i++ )
518 free( p_sys->render[i].psz_string );
519 p_sys->render[i].i_state = FBOSD_STATE_FREE;
522 #if defined(FBOSD_BLENDING)
523 if( p_sys->p_blend ) CloseBlending( p_intf );
525 if( p_sys->p_text ) CloseTextRenderer( p_intf );
528 image_HandlerDelete( p_sys->p_image );
529 if( p_sys->p_overlay )
530 p_sys->p_overlay->pf_release( p_sys->p_overlay );
532 free( p_sys->p_style );
536 #if defined(FBOSD_BLENDING)
537 static int OpenBlending( intf_thread_t *p_intf )
539 if( p_intf->p_sys->p_blend ) return VLC_EGENERIC;
541 p_intf->p_sys->p_blend =
542 vlc_object_create( p_intf, VLC_OBJECT_FILTER );
543 vlc_object_attach( p_intf->p_sys->p_blend, p_intf );
544 p_intf->p_sys->p_blend->fmt_out.video.i_x_offset =
545 p_intf->p_sys->p_blend->fmt_out.video.i_y_offset = 0;
546 p_intf->p_sys->p_blend->fmt_out.video.i_aspect =
547 p_intf->p_sys->fmt_out.i_aspect;
548 p_intf->p_sys->p_blend->fmt_out.video.i_chroma =
549 p_intf->p_sys->fmt_out.i_chroma;
550 if( config_GetInt( p_intf, "freetype-yuvp" ) )
551 p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
552 VLC_FOURCC('Y','U','V','P');
554 p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
555 VLC_FOURCC('Y','U','V','A');
557 p_intf->p_sys->p_blend->p_module =
558 module_Need( p_intf->p_sys->p_blend, "video blending", 0, 0 );
560 if( !p_intf->p_sys->p_blend->p_module )
566 static void CloseBlending( intf_thread_t *p_intf )
568 if( p_intf->p_sys->p_blend )
570 if( p_intf->p_sys->p_blend->p_module )
571 module_Unneed( p_intf->p_sys->p_blend,
572 p_intf->p_sys->p_blend->p_module );
574 vlc_object_detach( p_intf->p_sys->p_blend );
575 vlc_object_release( p_intf->p_sys->p_blend );
580 static int OpenTextRenderer( intf_thread_t *p_intf )
582 char *psz_modulename = NULL;
584 if( p_intf->p_sys->p_text ) return VLC_EGENERIC;
586 p_intf->p_sys->p_text =
587 vlc_object_create( p_intf, VLC_OBJECT_FILTER );
588 vlc_object_attach( p_intf->p_sys->p_text, p_intf );
590 p_intf->p_sys->p_text->fmt_out.video.i_width =
591 p_intf->p_sys->p_text->fmt_out.video.i_visible_width =
592 p_intf->p_sys->i_width;
593 p_intf->p_sys->p_text->fmt_out.video.i_height =
594 p_intf->p_sys->p_text->fmt_out.video.i_visible_height =
595 p_intf->p_sys->i_height;
597 psz_modulename = var_CreateGetString( p_intf, "text-renderer" );
598 if( psz_modulename && *psz_modulename )
600 p_intf->p_sys->p_text->p_module =
601 module_Need( p_intf->p_sys->p_text, "text renderer",
602 psz_modulename, true );
604 if( !p_intf->p_sys->p_text->p_module )
606 p_intf->p_sys->p_text->p_module =
607 module_Need( p_intf->p_sys->p_text, "text renderer", 0, 0 );
609 free( psz_modulename );
611 if( !p_intf->p_sys->p_text->p_module )
617 static void CloseTextRenderer( intf_thread_t *p_intf )
619 if( p_intf->p_sys->p_text )
621 if( p_intf->p_sys->p_text->p_module )
622 module_Unneed( p_intf->p_sys->p_text,
623 p_intf->p_sys->p_text->p_module );
625 vlc_object_detach( p_intf->p_sys->p_text );
626 vlc_object_release( p_intf->p_sys->p_text );
630 /*****************************************************************************
632 * allocate a picture buffer for use with the overlay fb.
633 *****************************************************************************/
634 static picture_t *AllocatePicture( vlc_object_t *p_this,
635 video_format_t *p_fmt )
637 picture_t *p_pic = malloc( sizeof( picture_t ) );
638 if( !p_pic ) return NULL;
640 if( !p_fmt->p_palette &&
641 ( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') ) )
643 p_fmt->p_palette = malloc( sizeof(video_palette_t) );
644 if( !p_fmt->p_palette )
650 else p_fmt->p_palette = NULL;
652 p_pic->p_data_orig = NULL;
654 vout_AllocatePicture( p_this, p_pic, p_fmt->i_chroma,
655 p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect );
657 if( !p_pic->i_planes )
660 free( p_fmt->p_palette );
666 /*****************************************************************************
668 * Deallocate a picture buffer and free all associated memory.
669 *****************************************************************************/
670 static void DeAllocatePicture( vlc_object_t *p_this, picture_t *p_pic,
671 video_format_t *p_fmt )
676 free( p_pic->p_data_orig );
677 if( p_pic->pf_release ) p_pic->pf_release( p_pic );
682 free( p_fmt->p_palette );
683 p_fmt->p_palette = NULL;
688 /*****************************************************************************
689 * SetOverlayTransparency: Set the transparency for this overlay fb,
690 * - true is make transparent
691 * - false is make non tranparent
692 *****************************************************************************/
693 static void SetOverlayTransparency( intf_thread_t *p_intf,
696 intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
697 size_t i_size = p_sys->fmt_out.i_width * p_sys->fmt_out.i_height
698 * p_sys->i_bytes_per_pixel;
699 size_t i_page_size = (p_sys->i_page_size > i_size) ?
700 i_size : p_sys->i_page_size;
702 if( p_sys->p_overlay )
704 msg_Dbg( p_intf, "Make overlay %s",
705 b_transparent ? "transparent" : "opaque" );
707 memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, i_page_size );
709 memset( p_sys->p_overlay->p[0].p_pixels, 0x00, i_page_size );
713 #if defined(FBOSD_BLENDING)
714 /*****************************************************************************
715 * BlendPicture: Blend two pictures together..
716 *****************************************************************************/
717 static int BlendPicture( intf_thread_t *p_intf, video_format_t *p_fmt_src,
718 video_format_t *p_fmt_dst, picture_t *p_pic_src,
719 picture_t *p_pic_dst )
721 intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
722 if( p_sys->p_blend && p_sys->p_blend->p_module )
724 int i_x_offset = p_sys->i_x;
725 int i_y_offset = p_sys->i_y;
727 memcpy( &p_sys->p_blend->fmt_in.video, p_fmt_src, sizeof( video_format_t ) );
729 /* Update the output picture size */
730 p_sys->p_blend->fmt_out.video.i_width =
731 p_sys->p_blend->fmt_out.video.i_visible_width =
733 p_sys->p_blend->fmt_out.video.i_height =
734 p_sys->p_blend->fmt_out.video.i_visible_height =
737 i_x_offset = __MAX( i_x_offset, 0 );
738 i_y_offset = __MAX( i_y_offset, 0 );
740 p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_pic_dst,
741 p_pic_src, p_pic_dst, i_x_offset, i_y_offset,
749 static int InvertAlpha( intf_thread_t *p_intf, picture_t **p_pic, video_format_t fmt )
751 uint8_t *p_begin = NULL, *p_end = NULL;
754 if( *p_pic && ((*p_pic)->i_planes != 1) )
757 "cannot invert alpha channel too many planes %d (only 1 supported)",
758 (*p_pic)->i_planes );
762 switch( fmt.i_chroma )
764 case VLC_FOURCC('R','V','2','4'):
765 p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
766 p_end = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
767 ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
770 case VLC_FOURCC('R','V','3','2'):
771 p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
772 p_end = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
773 ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
777 msg_Err( p_intf, "cannot invert alpha channel chroma not supported %4.4s",
778 (char *)&fmt.i_chroma );
782 for( ; p_begin < p_end; p_begin += i_skip )
786 if( i_opacity != 0xFF )
787 i_opacity = 255 - *p_begin;
788 *p_begin = i_opacity;
795 /*****************************************************************************
796 * RenderPicture: Render the picture into the p_dest buffer.
797 * We don't take transparent pixels into account, so we don't have to blend
798 * the two images together.
799 *****************************************************************************/
800 static int RenderPicture( intf_thread_t *p_intf, int i_x_offset, int i_y_offset,
801 picture_t *p_src, picture_t *p_dest )
804 VLC_UNUSED( p_intf );
806 if( !p_dest && !p_src ) return VLC_EGENERIC;
808 for( i = 0; i < p_src->i_planes ; i++ )
810 if( p_src->p[i].i_pitch == p_dest->p[i].i_pitch )
812 /* There are margins, but with the same width : perfect ! */
813 vlc_memcpy( p_dest->p[i].p_pixels, p_src->p[i].p_pixels,
814 p_src->p[i].i_pitch * p_src->p[i].i_visible_lines );
818 /* We need to proceed line by line */
819 uint8_t *p_in = p_src->p[i].p_pixels;
820 uint8_t *p_out = p_dest->p[i].p_pixels;
822 int i_x = i_x_offset * p_src->p[i].i_pixel_pitch;
823 int i_x_clip, i_y_clip;
825 /* Check boundaries, clip the image if necessary */
826 i_x_clip = ( i_x + p_src->p[i].i_visible_pitch ) - p_dest->p[i].i_visible_pitch;
827 i_x_clip = ( i_x_clip > 0 ) ? i_x_clip : 0;
829 i_y_clip = ( i_y_offset + p_src->p[i].i_visible_lines ) - p_dest->p[i].i_visible_lines;
830 i_y_clip = ( i_y_clip > 0 ) ? i_y_clip : 0;
831 #if defined(FBOSD_DEBUG)
832 msg_Dbg( p_intf, "i_pitch (%d,%d), (%d,%d)/(%d,%d)",
833 p_dest->p[i].i_visible_pitch, p_src->p[i].i_visible_pitch,
834 i_x_offset, i_y_offset, i_x, i_x_clip );
836 if( ( i_y_offset <= p_dest->p[i].i_visible_lines ) &&
837 ( i_x <= p_dest->p[i].i_visible_pitch ) )
841 p_out += ( i_y_offset * p_dest->p[i].i_pitch );
842 for( i_line = 0; i_line < ( p_src->p[i].i_visible_lines - i_y_clip ); i_line++ )
844 vlc_memcpy( p_out + i_x, p_in,
845 p_src->p[i].i_visible_pitch - i_x_clip );
846 p_in += p_src->p[i].i_pitch;
847 p_out += p_dest->p[i].i_pitch;
855 /*****************************************************************************
856 * RenderText - Render text to the desired picture format
857 *****************************************************************************/
858 static picture_t *RenderText( intf_thread_t *p_intf, const char *psz_string,
859 text_style_t *p_style, video_format_t *p_fmt )
861 intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
862 subpicture_region_t *p_region;
863 picture_t *p_dest = NULL;
865 if( !psz_string ) return p_dest;
867 if( p_sys->p_text && p_sys->p_text->p_module )
869 p_region = (subpicture_region_t *) malloc( sizeof(subpicture_region_t) );
873 memset( p_region, 0, sizeof(subpicture_region_t) );
875 p_region->psz_text = strdup( psz_string );
876 if( !p_region->psz_text )
881 p_region->p_style = p_style;
883 p_region->fmt.i_chroma = VLC_FOURCC('T','E','X','T');
884 p_region->fmt.i_aspect = 0;
885 p_region->fmt.i_width = p_region->fmt.i_visible_width = 0;
886 p_region->fmt.i_height = p_region->fmt.i_visible_height = 0;
887 p_region->fmt.i_x_offset = 0;
888 p_region->fmt.i_y_offset = 0;
890 p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
892 if( p_sys->p_text->pf_render_text )
894 video_format_t fmt_out;
896 memset( &fmt_out, 0, sizeof(video_format_t) );
898 p_sys->p_text->pf_render_text( p_sys->p_text,
899 p_region, p_region );
901 #if defined(FBOSD_BLENDING)
902 fmt_out = p_region->fmt;
903 fmt_out.i_bits_per_pixel = 32;
904 vlc_memcpy( p_fmt, &fmt_out, sizeof(video_format_t) );
906 p_dest = AllocatePicture( VLC_OBJECT(p_intf), &fmt_out );
909 if( p_region->picture.pf_release )
910 p_region->picture.pf_release( &p_region->picture );
911 free( p_region->psz_text );
915 vout_CopyPicture( VLC_OBJECT(p_intf), p_dest, &p_region->picture );
917 fmt_out.i_chroma = p_fmt->i_chroma;
918 p_dest = ConvertImage( p_intf, &p_region->picture,
919 &p_region->fmt, &fmt_out );
921 if( p_region->picture.pf_release )
922 p_region->picture.pf_release( &p_region->picture );
923 free( p_region->psz_text );
927 free( p_region->psz_text );
933 /*****************************************************************************
934 * LoadImage: Load an image from file into a picture buffer.
935 *****************************************************************************/
936 static picture_t *LoadImage( intf_thread_t *p_intf, video_format_t *p_fmt,
939 picture_t *p_pic = NULL;
941 if( psz_file && p_intf->p_sys->p_image )
943 video_format_t fmt_in, fmt_out;
945 memset( &fmt_in, 0, sizeof(fmt_in) );
946 memset( &fmt_out, 0, sizeof(fmt_out) );
948 fmt_out.i_chroma = p_fmt->i_chroma;
949 p_pic = image_ReadUrl( p_intf->p_sys->p_image, psz_file,
952 msg_Dbg( p_intf, "image size %dx%d chroma %4.4s",
953 fmt_out.i_width, fmt_out.i_height,
954 (char *)&p_fmt->i_chroma );
959 #if ! defined(FBOSD_BLENDING)
960 /*****************************************************************************
961 * Convertmage: Convert image to another fourcc
962 *****************************************************************************/
963 static picture_t *ConvertImage( intf_thread_t *p_intf, picture_t *p_pic,
964 video_format_t *p_fmt_in, video_format_t *p_fmt_out )
966 intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
967 picture_t *p_old = NULL;
971 p_old = image_Convert( p_sys->p_image, p_pic, p_fmt_in, p_fmt_out );
973 msg_Dbg( p_intf, "converted image size %dx%d chroma %4.4s",
974 p_fmt_out->i_width, p_fmt_out->i_height,
975 (char *)&p_fmt_out->i_chroma );
981 /*****************************************************************************
982 * Init: initialize framebuffer video thread output method
983 *****************************************************************************/
984 static int Init( intf_thread_t *p_intf )
986 intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
988 /* Initialize the output structure: RGB with square pixels, whatever
989 * the input format is, since it's the only format we know */
990 switch( p_sys->var_info.bits_per_pixel )
992 case 8: /* FIXME: set the palette */
993 p_sys->fmt_out.i_chroma = VLC_FOURCC('R','G','B','2'); break;
995 p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','5'); break;
997 p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','6'); break;
999 p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','2','4'); break;
1001 p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','3','2'); break;
1003 msg_Err( p_intf, "unknown screen depth %i",
1004 p_sys->var_info.bits_per_pixel );
1005 return VLC_EGENERIC;
1008 p_sys->fmt_out.i_bits_per_pixel = p_sys->var_info.bits_per_pixel;
1009 p_sys->fmt_out.i_width = p_sys->i_width;
1010 p_sys->fmt_out.i_height = p_sys->i_height;
1012 /* Assume we have square pixels */
1013 if( p_sys->i_aspect < 0 )
1015 p_sys->fmt_out.i_aspect = ( p_sys->i_width
1016 * VOUT_ASPECT_FACTOR ) / p_sys->i_height;
1018 else p_sys->fmt_out.i_aspect = p_sys->i_aspect;
1020 p_sys->fmt_out.i_sar_num = p_sys->fmt_out.i_sar_den = 1;
1022 /* Allocate overlay buffer */
1023 p_sys->p_overlay = AllocatePicture( VLC_OBJECT(p_intf),
1025 if( !p_sys->p_overlay ) return VLC_EGENERIC;
1027 SetOverlayTransparency( p_intf, true );
1029 /* We know the chroma, allocate a buffer which will be used
1030 * to write to the overlay framebuffer */
1031 p_sys->p_overlay->p->i_pixel_pitch = p_sys->i_bytes_per_pixel;
1032 p_sys->p_overlay->p->i_lines = p_sys->var_info.yres;
1033 p_sys->p_overlay->p->i_visible_lines = p_sys->var_info.yres;
1035 if( p_sys->var_info.xres_virtual )
1037 p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres_virtual
1038 * p_sys->i_bytes_per_pixel;
1042 p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres
1043 * p_sys->i_bytes_per_pixel;
1046 p_sys->p_overlay->p->i_visible_pitch = p_sys->var_info.xres
1047 * p_sys->i_bytes_per_pixel;
1049 p_sys->p_overlay->i_planes = 1;
1054 /*****************************************************************************
1055 * End: terminate framebuffer interface
1056 *****************************************************************************/
1057 static void End( intf_thread_t *p_intf )
1059 intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
1062 SetOverlayTransparency( p_intf, false );
1063 if( p_sys->p_overlay )
1066 ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1067 p_sys->i_page_size );
1069 msg_Err( p_intf, "unable to clear overlay" );
1072 DeAllocatePicture( VLC_OBJECT(p_intf), p_intf->p_sys->p_overlay,
1073 &p_intf->p_sys->fmt_out );
1074 p_intf->p_sys->p_overlay = NULL;
1077 /*****************************************************************************
1078 * OpenDisplay: initialize framebuffer
1079 *****************************************************************************/
1080 static int OpenDisplay( intf_thread_t *p_intf )
1082 intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
1083 char *psz_device; /* framebuffer device path */
1084 struct fb_fix_screeninfo fix_info; /* framebuffer fix information */
1086 /* Open framebuffer device */
1087 if( !(psz_device = config_GetPsz( p_intf, "fbosd-dev" )) )
1089 msg_Err( p_intf, "don't know which fb osd/overlay device to open" );
1090 return VLC_EGENERIC;
1093 p_sys->i_fd = open( psz_device, O_RDWR );
1094 if( p_sys->i_fd == -1 )
1096 msg_Err( p_intf, "cannot open %s (%s)", psz_device, strerror(errno) );
1098 return VLC_EGENERIC;
1102 /* Get framebuffer device information */
1103 if( ioctl( p_sys->i_fd, FBIOGET_VSCREENINFO, &p_sys->var_info ) )
1105 msg_Err( p_intf, "cannot get fb info (%s)", strerror(errno) );
1106 close( p_sys->i_fd );
1107 return VLC_EGENERIC;
1110 /* Get some info on the framebuffer itself */
1111 if( ioctl( p_sys->i_fd, FBIOGET_FSCREENINFO, &fix_info ) == 0 )
1113 p_sys->i_width = p_sys->fmt_out.i_width = p_sys->var_info.xres;
1114 p_sys->i_height = p_sys->fmt_out.i_height = p_sys->var_info.yres;
1117 /* FIXME: if the image is full-size, it gets cropped on the left
1118 * because of the xres / xres_virtual slight difference */
1119 msg_Dbg( p_intf, "%ix%i (virtual %ix%i)",
1120 p_sys->var_info.xres, p_sys->var_info.yres,
1121 p_sys->var_info.xres_virtual,
1122 p_sys->var_info.yres_virtual );
1124 p_sys->fmt_out.i_width = p_sys->i_width;
1125 p_sys->fmt_out.i_height = p_sys->i_height;
1127 p_sys->p_palette = NULL;
1128 p_sys->b_pan = ( fix_info.ypanstep || fix_info.ywrapstep );
1130 switch( p_sys->var_info.bits_per_pixel )
1133 p_sys->p_palette = malloc( 8 * 256 * sizeof( uint16_t ) );
1134 if( !p_sys->p_palette )
1136 close( p_sys->i_fd );
1139 p_sys->fb_cmap.start = 0;
1140 p_sys->fb_cmap.len = 256;
1141 p_sys->fb_cmap.red = p_sys->p_palette;
1142 p_sys->fb_cmap.green = p_sys->p_palette + 256 * sizeof( uint16_t );
1143 p_sys->fb_cmap.blue = p_sys->p_palette + 2 * 256 * sizeof( uint16_t );
1144 p_sys->fb_cmap.transp = p_sys->p_palette + 3 * 256 * sizeof( uint16_t );
1146 /* Save the colormap */
1147 ioctl( p_sys->i_fd, FBIOGETCMAP, &p_sys->fb_cmap );
1149 p_sys->i_bytes_per_pixel = 1;
1154 p_sys->i_bytes_per_pixel = 2;
1158 p_sys->i_bytes_per_pixel = 3;
1162 p_sys->i_bytes_per_pixel = 4;
1166 msg_Err( p_intf, "screen depth %d is not supported",
1167 p_sys->var_info.bits_per_pixel );
1169 close( p_sys->i_fd );
1170 return VLC_EGENERIC;
1173 p_sys->i_page_size = p_sys->i_width * p_sys->i_height
1174 * p_sys->i_bytes_per_pixel;
1176 msg_Dbg( p_intf, "framebuffer type=%d, visual=%d, ypanstep=%d, "
1177 "ywrap=%d, accel=%d", fix_info.type, fix_info.visual,
1178 fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
1182 /*****************************************************************************
1183 * CloseDisplay: terminate FB interface thread
1184 *****************************************************************************/
1185 static void CloseDisplay( intf_thread_t *p_intf )
1187 intf_sys_t *p_sys = (intf_sys_t *) p_intf;
1189 /* Restore palette */
1190 if( p_sys->var_info.bits_per_pixel == 8 )
1192 ioctl( p_sys->i_fd, FBIOPUTCMAP, &p_sys->fb_cmap );
1193 free( p_sys->p_palette );
1194 p_sys->p_palette = NULL;
1198 close( p_sys->i_fd );
1201 static void Render( intf_thread_t *p_intf, struct fbosd_render_t *render )
1203 intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1205 if( render->i_state != FBOSD_STATE_RENDER ) return;
1206 if( !render->psz_string ) return;
1208 if( render->i_type == FBOSD_RENDER_IMAGE )
1211 p_pic = LoadImage( p_intf, &p_sys->fmt_out, render->psz_string );
1214 RenderPicture( p_intf, render->i_x, render->i_y,
1215 p_pic, p_sys->p_overlay );
1216 p_pic->pf_release( p_pic );
1219 else if( render->i_type == FBOSD_RENDER_TEXT )
1222 #if defined(FBOSD_BLENDING)
1223 video_format_t fmt_in;
1224 memset( &fmt_in, 0, sizeof(video_format_t) );
1225 p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1229 BlendPicture( p_intf, &fmt_in, &p_sys->fmt_out,
1230 p_text, p_sys->p_overlay );
1231 msg_Dbg( p_intf, "releasing picture" );
1232 DeAllocatePicture( VLC_OBJECT( p_intf ), p_text, &fmt_in );
1235 p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1239 RenderPicture( p_intf, render->i_x, render->i_y,
1240 p_text, p_sys->p_overlay );
1241 p_text->pf_release( p_text );
1247 static void RenderClear( intf_thread_t *p_intf, struct fbosd_render_t *render )
1249 intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1251 vlc_memcpy( &render->text_style, &default_text_style,
1252 sizeof( text_style_t ) );
1253 free( render->psz_string );
1254 render->psz_string = NULL;
1256 render->i_x = p_sys->i_x;
1257 render->i_y = p_sys->i_y;
1258 render->i_pos = p_sys->i_pos;
1259 render->i_alpha = p_sys->i_alpha;
1260 render->b_absolute = p_sys->b_absolute;
1261 render->i_state = FBOSD_STATE_FREE;
1264 static bool isRendererReady( intf_thread_t *p_intf )
1266 intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1269 /* Check if there are more items to render */
1270 for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1272 if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1278 /*****************************************************************************
1280 *****************************************************************************
1281 * This part of the interface is in a separate thread so that we can call
1282 * exec() from within it without annoying the rest of the program.
1283 *****************************************************************************/
1284 static void Run( intf_thread_t *p_intf )
1286 intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1288 while( !intf_ShouldDie( p_intf ) )
1292 /* Is there somthing to render? */
1293 for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1295 if( p_sys->render[i].i_state == FBOSD_STATE_RENDER )
1297 Render( p_intf, &p_sys->render[i] );
1298 RenderClear( p_intf, &p_sys->render[i] );
1302 if( p_sys->b_clear )
1304 SetOverlayTransparency( p_intf, true );
1306 var_SetString( p_intf, "fbosd-image", "" );
1307 var_SetString( p_intf, "fbosd-text", "" );
1309 p_sys->b_clear = false;
1310 p_sys->b_need_update = true;
1313 if( p_sys->b_need_update && p_sys->p_overlay &&
1314 isRendererReady( p_intf ) )
1317 #if defined(FBOSD_BLENDING)
1318 /* Reverse alpha channel to work around FPGA bug */
1319 InvertAlpha( p_intf, &p_sys->p_overlay, p_sys->fmt_out );
1321 ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1322 p_sys->i_page_size );
1324 msg_Err( p_intf, "unable to write to overlay" );
1325 lseek( p_sys->i_fd, 0, SEEK_SET );
1327 /* clear the picture */
1328 memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, p_sys->i_page_size );
1329 p_sys->b_need_update = false;
1332 if( vlc_CPU() & CPU_CAPABILITY_FPU )
1333 msleep( INTF_IDLE_SLEEP );
1341 static int OverlayCallback( vlc_object_t *p_this, char const *psz_cmd,
1342 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1344 intf_thread_t *p_intf = (intf_thread_t *) p_this;
1345 intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1346 VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1348 if( !strncmp( psz_cmd, "fbosd-display", 13 ) )
1350 p_sys->b_need_update = true;
1352 else if( !strncmp( psz_cmd, "fbosd-clear", 11 ) )
1355 /* Clear the entire render list */
1356 for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1358 RenderClear( p_intf, &p_sys->render[i] );
1360 p_sys->b_clear = true;
1362 else if( !strncmp( psz_cmd, "fbosd-render", 12 ) )
1365 /* Are we already busy with on slot ? */
1366 for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1368 if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1370 p_sys->render[i].i_state = FBOSD_STATE_RENDER;
1378 /* Are we already busy with on slot ? */
1379 for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1381 if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1384 /* No, then find first FREE slot */
1385 if( p_sys->render[i].i_state != FBOSD_STATE_RESERVED )
1387 for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1389 if( p_sys->render[i].i_state == FBOSD_STATE_FREE )
1392 if( p_sys->render[i].i_state != FBOSD_STATE_FREE )
1394 msg_Warn( p_this, "render space depleated" );
1398 /* Found a free slot */
1399 p_sys->render[i].i_state = FBOSD_STATE_RESERVED;
1400 if( !strncmp( psz_cmd, "fbosd-image", 11 ) )
1402 free( p_sys->render[i].psz_string );
1403 p_sys->render[i].psz_string = strdup( newval.psz_string );
1404 p_sys->render[i].i_type = FBOSD_RENDER_IMAGE;
1406 else if( !strncmp( psz_cmd, "fbosd-text", 10 ) )
1408 free( p_sys->render[i].psz_string );
1409 p_sys->render[i].psz_string = strdup( newval.psz_string );
1410 p_sys->render[i].i_type = FBOSD_RENDER_TEXT;
1412 else if( !strncmp( psz_cmd, "fbosd-x", 7 ) )
1414 p_sys->render[i].b_absolute = false;
1415 p_sys->render[i].i_x = (newval.i_int < p_sys->i_width) ?
1416 newval.i_int : p_sys->i_width;
1418 else if( !strncmp( psz_cmd, "fbosd-y", 7 ) )
1420 p_sys->render[i].b_absolute = false;
1421 p_sys->render[i].i_y = (newval.i_int < p_sys->i_height) ?
1422 newval.i_int : p_sys->i_height;
1424 else if( !strncmp( psz_cmd, "fbosd-position", 14 ) )
1426 p_sys->render[i].b_absolute = true;
1427 p_sys->render[i].i_pos = newval.i_int;
1429 else if( !strncmp( psz_cmd, "fbosd-font-size", 15 ) )
1431 p_sys->render[i].text_style.i_font_size = newval.i_int;
1433 else if( !strncmp( psz_cmd, "fbosd-font-color", 16 ) )
1435 p_sys->render[i].text_style.i_font_color = newval.i_int;
1437 else if( !strncmp( psz_cmd, "fbosd-font-opacity", 18 ) )
1439 p_sys->render[i].text_style.i_font_alpha = 255 - newval.i_int;
1441 #if defined(FBOSD_BLENDING)
1442 else if( !strncmp( psz_cmd, "fbosd-alpha", 11 ) )
1444 p_sys->render[i].i_alpha = newval.i_int;