]> git.sesse.net Git - vlc/blob - modules/gui/fbosd.c
Framebuffer OSD: path from VLC config, need utf8_open
[vlc] / modules / gui / fbosd.c
1 /*****************************************************************************
2  * fbosd.c : framebuffer osd plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2007-2008, the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jean-Paul Saman
8  * Copied from modules/video_output/fb.c by Samuel Hocevar <sam@zoy.org>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_charset.h>
35
36 #include <errno.h>
37 #include <stdlib.h>                                                /* free() */
38 #include <string.h>                                            /* strerror() */
39 #include <fcntl.h>                                                 /* open() */
40 #include <unistd.h>                                               /* close() */
41
42 #include <sys/ioctl.h>
43 #include <sys/mman.h>                                              /* mmap() */
44
45 #include <linux/fb.h>
46
47 #include <vlc_image.h>
48 #include <vlc_interface.h>
49 #include <vlc_input.h>
50 #include <vlc_vout.h>
51 #include <vlc_filter.h>
52 #include <vlc_osd.h>
53 #include <vlc_strings.h>
54
55 #undef FBOSD_BLENDING
56 #undef FBOSD_DEBUG
57
58 /*****************************************************************************
59  * Local prototypes
60  *****************************************************************************/
61 static int  Create    ( vlc_object_t * );
62 static void Destroy   ( vlc_object_t * );
63 static void Run       ( intf_thread_t * );
64
65 static int  Init      ( intf_thread_t * );
66 static void End       ( intf_thread_t * );
67
68 static int  OpenDisplay    ( intf_thread_t * );
69 static void CloseDisplay   ( intf_thread_t * );
70
71 /* Load modules needed for rendering and blending */
72 #if defined(FBOSD_BLENDING)
73 static int  OpenBlending     ( intf_thread_t * );
74 static void CloseBlending    ( intf_thread_t * );
75 #endif
76 static int  OpenTextRenderer ( intf_thread_t * );
77 static void CloseTextRenderer( intf_thread_t * );
78
79 /* Manipulate the overlay buffer */
80 static int  OverlayCallback( vlc_object_t *, char const *,
81                              vlc_value_t, vlc_value_t, void * );
82
83 static picture_t *AllocatePicture( video_format_t * );
84 static void DeAllocatePicture( picture_t *, video_format_t * );
85 static void SetOverlayTransparency( intf_thread_t *,
86                                     bool );
87 static picture_t *LoadImage( intf_thread_t *, video_format_t *,
88                              char * );
89
90 #if defined(FBOSD_BLENDING)
91 static int BlendPicture( intf_thread_t *, video_format_t *,
92                          video_format_t *, picture_t *, picture_t * );
93 #else
94 static picture_t *ConvertImage( intf_thread_t *, picture_t *,
95                                 video_format_t *, video_format_t * );
96 #endif
97 static int RenderPicture( intf_thread_t *, int, int,
98                           picture_t *, picture_t * );
99 static picture_t *RenderText( intf_thread_t *, const char *,
100                               text_style_t *, video_format_t * );
101
102 #define DEVICE_TEXT N_("Framebuffer device")
103 #define DEVICE_LONGTEXT N_( \
104     "Framebuffer device to use for rendering (usually /dev/fb0).")
105
106 #define ASPECT_RATIO_TEXT N_("Video aspect ratio")
107 #define ASPECT_RATIO_LONGTEXT N_( \
108     "Aspect ratio of the video image (4:3, 16:9). Default is square pixels." )
109
110 #define FBOSD_IMAGE_TEXT N_("Image file")
111 #define FBOSD_IMAGE_LONGTEXT N_( \
112     "Filename of image file to use on the overlay framebuffer." )
113
114 #define ALPHA_TEXT N_("Transparency of the image")
115 #define ALPHA_LONGTEXT N_( "Transparency value of the new image " \
116     "used in blending. By default it set to fully opaque (255). " \
117     "(from 0 for full transparency to 255 for full opacity)" )
118
119 #define FBOSD_TEXT N_("Text")
120 #define FBOSD_LONGTEXT N_( "Text to display on the overlay framebuffer." )
121
122 #define POSX_TEXT N_("X coordinate")
123 #define POSX_LONGTEXT N_("X coordinate of the rendered image")
124
125 #define POSY_TEXT N_("Y coordinate")
126 #define POSY_LONGTEXT N_("Y coordinate of the rendered image")
127
128 #define POS_TEXT N_("Position")
129 #define POS_LONGTEXT N_( \
130   "You can enforce the picture position on the overlay " \
131   "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
132   "also use combinations of these values, e.g. 6=top-right).")
133
134 #define OPACITY_TEXT N_("Opacity")
135 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
136     "overlayed text. 0 = transparent, 255 = totally opaque. " )
137
138 #define SIZE_TEXT N_("Font size, pixels")
139 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
140     "font size)." )
141
142 #define COLOR_TEXT N_("Color")
143 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
144     "the video. This must be an hexadecimal (like HTML colors). The first two "\
145     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
146     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
147
148 #define CLEAR_TEXT N_( "Clear overlay framebuffer" )
149 #define CLEAR_LONGTEXT N_( "The displayed overlay images is cleared by " \
150     "making the overlay completely transparent. All previously rendered " \
151     "images and text will be cleared from the cache." )
152
153 #define RENDER_TEXT N_( "Render text or image" )
154 #define RENDER_LONGTEXT N_( "Render the image or text in current overlay " \
155     "buffer." )
156
157 #define DISPLAY_TEXT N_( "Display on overlay framebuffer" )
158 #define DISPLAY_LONGTEXT N_( "All rendered images and text will be " \
159     "displayed on the overlay framebuffer." )
160
161 static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
162 static const char *const ppsz_pos_descriptions[] =
163 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
164   N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
165
166 static const int pi_color_values[] = {
167                0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
168                0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
169                0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
170                0x00000080, 0x000000FF, 0x0000FFFF};
171 static const char *const ppsz_color_descriptions[] = {
172                N_("Default"), N_("Black"),
173                N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
174                N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
175                N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
176                N_("Aqua") };
177
178 vlc_module_begin ()
179     set_shortname( "fbosd" )
180     set_category( CAT_INTERFACE )
181     set_subcategory( SUBCAT_INTERFACE_MAIN )
182
183     add_file( "fbosd-dev", "/dev/fb0", NULL, DEVICE_TEXT, DEVICE_LONGTEXT,
184               false )
185     add_string( "fbosd-aspect-ratio", "", NULL, ASPECT_RATIO_TEXT,
186                 ASPECT_RATIO_LONGTEXT, true )
187
188     add_string( "fbosd-image", NULL, NULL, FBOSD_IMAGE_TEXT,
189                 FBOSD_IMAGE_LONGTEXT, true )
190     add_string( "fbosd-text", NULL, NULL, FBOSD_TEXT,
191                 FBOSD_LONGTEXT, true )
192
193     add_integer_with_range( "fbosd-alpha", 255, 0, 255, NULL, ALPHA_TEXT,
194                             ALPHA_LONGTEXT, true )
195
196     set_section( N_("Position"), NULL )
197     add_integer( "fbosd-x", 0, NULL, POSX_TEXT,
198                  POSX_LONGTEXT, false )
199     add_integer( "fbosd-y", 0, NULL, POSY_TEXT,
200                  POSY_LONGTEXT, false )
201     add_integer( "fbosd-position", 8, NULL, POS_TEXT, POS_LONGTEXT, true )
202         change_integer_list( pi_pos_values, ppsz_pos_descriptions, NULL );
203
204     set_section( N_("Font"), NULL )
205     add_integer_with_range( "fbosd-font-opacity", 255, 0, 255, NULL,
206         OPACITY_TEXT, OPACITY_LONGTEXT, false )
207     add_integer( "fbosd-font-color", 0x00FFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
208                  false )
209         change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
210     add_integer( "fbosd-font-size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT,
211                  false )
212
213     set_section( N_("Commands"), NULL )
214     add_bool( "fbosd-clear", false, NULL, CLEAR_TEXT, CLEAR_LONGTEXT, true )
215     add_bool( "fbosd-render", false, NULL, RENDER_TEXT, RENDER_LONGTEXT, true )
216     add_bool( "fbosd-display", false, NULL, DISPLAY_TEXT, DISPLAY_LONGTEXT, true )
217
218     set_description( N_("GNU/Linux osd/overlay framebuffer interface") )
219     set_capability( "interface", 10 )
220     set_callbacks( Create, Destroy )
221 vlc_module_end ()
222
223 /*****************************************************************************
224  * fbosd_render_t: render descriptor
225  *****************************************************************************/
226 struct fbosd_render_t
227 {
228 #define FBOSD_RENDER_IMAGE 0
229 #define FBOSD_RENDER_TEXT  1
230     int             i_type;
231
232 #define FBOSD_STATE_FREE     0
233 #define FBOSD_STATE_RESERVED 1
234 #define FBOSD_STATE_RENDER   2
235     int             i_state;
236
237     /* Font style */
238     text_style_t*   p_text_style;                            /* font control */
239     char            *psz_string;
240
241     /* Position */
242     bool            b_absolute;
243     int             i_x;
244     int             i_y;
245     int             i_pos;
246     int             i_alpha;                      /* transparency for images */
247 };
248 #define FBOSD_RENDER_MAX 10
249
250 /*****************************************************************************
251  * intf_sys_t: interface framebuffer method descriptor
252  *****************************************************************************/
253 struct intf_sys_t
254 {
255     /* Framebuffer information */
256     int                         i_fd;                       /* device handle */
257     struct fb_var_screeninfo    var_info;        /* current mode information */
258     bool                  b_pan;     /* does device supports panning ? */
259     struct fb_cmap              fb_cmap;                /* original colormap */
260     uint16_t                    *p_palette;              /* original palette */
261
262     /* Overlay framebuffer format */
263     video_format_t  fmt_out;
264     picture_t       *p_overlay;
265     size_t          i_page_size;                                /* page size */
266     int             i_width;
267     int             i_height;
268     int             i_aspect;
269     int             i_bytes_per_pixel;
270
271     /* Image and Picture rendering */
272     image_handler_t *p_image;
273 #if defined(FBOSD_BLENDING)
274     filter_t *p_blend;                              /* alpha blending module */
275 #endif
276     filter_t *p_text;                                /* text renderer module */
277
278     /* Render */
279     struct fbosd_render_t render[FBOSD_RENDER_MAX];
280
281     /* Font style */
282     text_style_t    *p_style;                                /* font control */
283
284     /* Position */
285     bool      b_absolute;
286     int       i_x;
287     int       i_y;
288     int       i_pos;
289
290     int       i_alpha;                      /* transparency for images */
291
292     /* commands control */
293     bool      b_need_update;    /* update display with \overlay buffer */
294     bool      b_clear;      /* clear overlay buffer make it tranparent */
295     bool      b_render;   /* render an image or text in overlay buffer */
296 };
297
298 /*****************************************************************************
299  * Create: allocates FB interface thread output method
300  *****************************************************************************/
301 static int Create( vlc_object_t *p_this )
302 {
303     intf_thread_t *p_intf = (intf_thread_t *)p_this;
304     intf_sys_t    *p_sys;
305     char          *psz_aspect;
306     char          *psz_tmp;
307     int i;
308
309     /* Allocate instance and initialize some members */
310     p_intf->p_sys = p_sys = calloc( 1, sizeof( intf_sys_t ) );
311     if( !p_intf->p_sys )
312         return VLC_ENOMEM;
313
314     p_sys->p_style = text_style_New();
315     if( !p_sys->p_style )
316     {
317         free( p_intf->p_sys );
318         return VLC_ENOMEM;
319     }
320
321     p_intf->pf_run = Run;
322
323     p_sys->p_image = image_HandlerCreate( p_this );
324     if( !p_sys->p_image )
325     {
326         text_style_Delete( p_sys->p_style );
327         free( p_sys );
328         return VLC_ENOMEM;
329     }
330
331     p_sys->i_alpha = var_CreateGetIntegerCommand( p_intf, "fbosd-alpha" );
332     var_AddCallback( p_intf, "fbosd-alpha", OverlayCallback, NULL );
333
334     p_sys->i_aspect = -1;
335     psz_aspect =
336             var_CreateGetNonEmptyString( p_intf, "fbosd-aspect-ratio" );
337     if( psz_aspect )
338     {
339         char *psz_parser = strchr( psz_aspect, ':' );
340
341         if( psz_parser )
342         {
343             *psz_parser++ = '\0';
344             p_sys->i_aspect = ( atoi( psz_aspect )
345                               * VOUT_ASPECT_FACTOR ) / atoi( psz_parser );
346             p_sys->fmt_out.i_aspect = p_sys->i_aspect;
347         }
348         msg_Dbg( p_intf, "using aspect ratio %d:%d",
349                   atoi( psz_aspect ), atoi( psz_parser ) );
350
351         free( psz_aspect );
352     }
353
354     /* Use PAL by default */
355     p_sys->i_width  = p_sys->fmt_out.i_width  = 704;
356     p_sys->i_height = p_sys->fmt_out.i_height = 576;
357
358     psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-image" );
359     var_AddCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
360     if( psz_tmp && *psz_tmp )
361     {
362         p_sys->render[0].i_type = FBOSD_RENDER_IMAGE;
363         p_sys->render[0].i_state = FBOSD_STATE_RENDER;
364         p_sys->render[0].psz_string = strdup( psz_tmp );
365     }
366     free( psz_tmp );
367
368     psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-text" );
369     var_AddCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
370     if( psz_tmp && *psz_tmp )
371     {
372         p_sys->render[1].i_type = FBOSD_RENDER_TEXT;
373         p_sys->render[1].i_state = FBOSD_STATE_RENDER;
374         p_sys->render[1].psz_string = strdup( psz_tmp );
375     }
376     free( psz_tmp );
377
378     p_sys->i_pos = var_CreateGetIntegerCommand( p_intf, "fbosd-position" );
379     p_sys->i_x = var_CreateGetIntegerCommand( p_intf, "fbosd-x" );
380     p_sys->i_y = var_CreateGetIntegerCommand( p_intf, "fbosd-y" );
381
382     var_AddCallback( p_intf, "fbosd-position", OverlayCallback, NULL );
383     var_AddCallback( p_intf, "fbosd-x", OverlayCallback, NULL );
384     var_AddCallback( p_intf, "fbosd-y", OverlayCallback, NULL );
385
386     p_sys->p_style->i_font_size =
387             var_CreateGetIntegerCommand( p_intf, "fbosd-font-size" );
388     p_sys->p_style->i_font_color =
389             var_CreateGetIntegerCommand( p_intf, "fbosd-font-color" );
390     p_sys->p_style->i_font_alpha = 255 -
391             var_CreateGetIntegerCommand( p_intf, "fbosd-font-opacity" );
392
393     var_AddCallback( p_intf, "fbosd-font-color", OverlayCallback, NULL );
394     var_AddCallback( p_intf, "fbosd-font-size", OverlayCallback, NULL );
395     var_AddCallback( p_intf, "fbosd-font-opacity", OverlayCallback, NULL );
396
397     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
398         p_sys->render[i].p_text_style = text_style_New();
399
400     p_sys->b_clear = var_CreateGetBoolCommand( p_intf, "fbosd-clear" );
401     p_sys->b_render = var_CreateGetBoolCommand( p_intf, "fbosd-render" );
402     p_sys->b_need_update = var_CreateGetBoolCommand( p_intf, "fbosd-display" );
403
404     var_AddCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
405     var_AddCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
406     var_AddCallback( p_intf, "fbosd-display", OverlayCallback, NULL );
407
408     /* Check if picture position was overridden */
409     p_sys->b_absolute = true;
410     if( ( p_sys->i_x >= 0 ) && ( p_sys->i_y >= 0 ) )
411     {
412         p_sys->b_absolute = false;
413         p_sys->i_y = (p_sys->i_y < p_sys->i_height) ?
414                         p_sys->i_y : p_sys->i_height;
415         p_sys->i_x = (p_sys->i_x < p_sys->i_width) ?
416                         p_sys->i_x : p_sys->i_width;
417     }
418
419     p_sys->render[0].i_x = p_sys->render[1].i_x = p_sys->i_x;
420     p_sys->render[0].i_y = p_sys->render[1].i_y = p_sys->i_y;
421     p_sys->render[0].i_pos = p_sys->render[1].i_pos = p_sys->i_pos;
422     p_sys->render[0].i_alpha = p_sys->render[1].i_alpha = p_sys->i_alpha;
423
424     /* Initialize framebuffer */
425     if( OpenDisplay( p_intf ) )
426     {
427         Destroy( VLC_OBJECT(p_intf) );
428         return VLC_EGENERIC;
429     }
430
431     Init( p_intf );
432
433 #if defined(FBOSD_BLENDING)
434     /* Load the blending module */
435     if( OpenBlending( p_intf ) )
436     {
437         msg_Err( p_intf, "Unable to load image blending module" );
438         Destroy( VLC_OBJECT(p_intf) );
439         return VLC_EGENERIC;
440     }
441 #endif
442
443     /* Load text renderer module */
444     if( OpenTextRenderer( p_intf ) )
445     {
446         msg_Err( p_intf, "Unable to load text rendering module" );
447         Destroy( VLC_OBJECT(p_intf) );
448         return VLC_EGENERIC;
449     }
450
451     p_sys->b_render = true;
452     p_sys->b_need_update = true;
453
454     return VLC_SUCCESS;
455 }
456
457 /*****************************************************************************
458  * Destroy: destroy FB interface thread output method
459  *****************************************************************************
460  * Terminate an output method created by Create
461  *****************************************************************************/
462 static void Destroy( vlc_object_t *p_this )
463 {
464     intf_thread_t *p_intf = (intf_thread_t *)p_this;
465     intf_sys_t *p_sys = p_intf->p_sys;
466     int i;
467
468
469     p_sys->b_need_update = false;
470     p_sys->b_render = false;
471     p_sys->b_clear = false;
472
473     var_DelCallback( p_intf, "fbosd-alpha", OverlayCallback, NULL );
474     var_Destroy( p_intf, "fbosd-alpha" );
475
476     var_DelCallback( p_intf, "fbosd-x", OverlayCallback, NULL );
477     var_DelCallback( p_intf, "fbosd-y", OverlayCallback, NULL );
478     var_DelCallback( p_intf, "fbosd-position", OverlayCallback, NULL );
479     var_DelCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
480     var_DelCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
481     var_DelCallback( p_intf, "fbosd-font-size", OverlayCallback, NULL );
482     var_DelCallback( p_intf, "fbosd-font-color", OverlayCallback, NULL );
483     var_DelCallback( p_intf, "fbosd-font-opacity", OverlayCallback, NULL );
484     var_DelCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
485     var_DelCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
486     var_DelCallback( p_intf, "fbosd-display", OverlayCallback, NULL );
487
488     var_Destroy( p_intf, "fbosd-x" );
489     var_Destroy( p_intf, "fbosd-y" );
490     var_Destroy( p_intf, "fbosd-position" );
491     var_Destroy( p_intf, "fbosd-image" );
492     var_Destroy( p_intf, "fbosd-text" );
493     var_Destroy( p_intf, "fbosd-font-size" );
494     var_Destroy( p_intf, "fbosd-font-color" );
495     var_Destroy( p_intf, "fbosd-font-opacity" );
496     var_Destroy( p_intf, "fbosd-clear" );
497     var_Destroy( p_intf, "fbosd-render" );
498     var_Destroy( p_intf, "fbosd-display" );
499
500     var_Destroy( p_intf, "fbosd-aspect-ratio" );
501
502     CloseDisplay( p_intf );
503
504     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
505     {
506         free( p_sys->render[i].psz_string );
507         p_sys->render[i].i_state = FBOSD_STATE_FREE;
508         text_style_Delete( p_sys->render[i].p_text_style );
509     }
510
511 #if defined(FBOSD_BLENDING)
512     if( p_sys->p_blend ) CloseBlending( p_intf );
513 #endif
514     if( p_sys->p_text )  CloseTextRenderer( p_intf );
515
516     if( p_sys->p_image )
517         image_HandlerDelete( p_sys->p_image );
518     if( p_sys->p_overlay )
519         picture_Release( p_sys->p_overlay );
520
521     text_style_Delete( p_sys->p_style );
522     free( p_sys );
523 }
524
525 #if defined(FBOSD_BLENDING)
526 static int OpenBlending( intf_thread_t *p_intf )
527 {
528     if( p_intf->p_sys->p_blend ) return VLC_EGENERIC;
529
530     p_intf->p_sys->p_blend =
531             vlc_object_create( p_intf, sizeof(filter_t) );
532     vlc_object_attach( p_intf->p_sys->p_blend, p_intf );
533     p_intf->p_sys->p_blend->fmt_out.video.i_x_offset =
534         p_intf->p_sys->p_blend->fmt_out.video.i_y_offset = 0;
535     p_intf->p_sys->p_blend->fmt_out.video.i_aspect =
536             p_intf->p_sys->fmt_out.i_aspect;
537     p_intf->p_sys->p_blend->fmt_out.video.i_chroma =
538             p_intf->p_sys->fmt_out.i_chroma;
539     if( config_GetInt( p_intf, "freetype-yuvp" ) )
540         p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
541                 VLC_CODEC_YUVP;
542     else
543         p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
544                 VLC_CODEC_YUVA;
545
546     p_intf->p_sys->p_blend->p_module =
547         module_need( p_intf->p_sys->p_blend, "video blending", NULL, false );
548
549     if( !p_intf->p_sys->p_blend->p_module )
550         return VLC_EGENERIC;
551
552     return VLC_SUCCESS;
553 }
554
555 static void CloseBlending( intf_thread_t *p_intf )
556 {
557     if( p_intf->p_sys->p_blend )
558     {
559         if( p_intf->p_sys->p_blend->p_module )
560             module_unneed( p_intf->p_sys->p_blend,
561                            p_intf->p_sys->p_blend->p_module );
562
563         vlc_object_detach( p_intf->p_sys->p_blend );
564         vlc_object_release( p_intf->p_sys->p_blend );
565     }
566 }
567 #endif
568
569 static int OpenTextRenderer( intf_thread_t *p_intf )
570 {
571     char *psz_modulename = NULL;
572
573     if( p_intf->p_sys->p_text ) return VLC_EGENERIC;
574
575     p_intf->p_sys->p_text =
576             vlc_object_create( p_intf, sizeof(filter_t) );
577     vlc_object_attach( p_intf->p_sys->p_text, p_intf );
578
579     p_intf->p_sys->p_text->fmt_out.video.i_width =
580         p_intf->p_sys->p_text->fmt_out.video.i_visible_width =
581         p_intf->p_sys->i_width;
582     p_intf->p_sys->p_text->fmt_out.video.i_height =
583         p_intf->p_sys->p_text->fmt_out.video.i_visible_height =
584         p_intf->p_sys->i_height;
585
586     psz_modulename = var_CreateGetString( p_intf, "text-renderer" );
587     if( psz_modulename && *psz_modulename )
588     {
589         p_intf->p_sys->p_text->p_module =
590             module_need( p_intf->p_sys->p_text, "text renderer",
591                             psz_modulename, true );
592     }
593     if( !p_intf->p_sys->p_text->p_module )
594     {
595         p_intf->p_sys->p_text->p_module =
596             module_need( p_intf->p_sys->p_text, "text renderer", NULL, false );
597     }
598     free( psz_modulename );
599
600     if( !p_intf->p_sys->p_text->p_module )
601         return VLC_EGENERIC;
602
603     return VLC_SUCCESS;
604 }
605
606 static void CloseTextRenderer( intf_thread_t *p_intf )
607 {
608     if( p_intf->p_sys->p_text )
609     {
610         if( p_intf->p_sys->p_text->p_module )
611             module_unneed( p_intf->p_sys->p_text,
612                            p_intf->p_sys->p_text->p_module );
613
614         vlc_object_detach( p_intf->p_sys->p_text );
615         vlc_object_release( p_intf->p_sys->p_text );
616     }
617 }
618
619 /*****************************************************************************
620  * AllocatePicture:
621  * allocate a picture buffer for use with the overlay fb.
622  *****************************************************************************/
623 static picture_t *AllocatePicture( video_format_t *p_fmt )
624 {
625     picture_t *p_picture = picture_New( p_fmt->i_chroma,
626                                         p_fmt->i_width, p_fmt->i_height,
627                                         p_fmt->i_aspect );
628     if( !p_picture )
629         return NULL;
630
631     if( !p_fmt->p_palette &&
632         ( p_fmt->i_chroma == VLC_CODEC_YUVP ) )
633     {
634         p_fmt->p_palette = malloc( sizeof(video_palette_t) );
635         if( !p_fmt->p_palette )
636         {
637             picture_Release( p_picture );
638             return NULL;
639         }
640     }
641     else
642     {
643         p_fmt->p_palette = NULL;
644     }
645
646     return p_picture;
647 }
648
649 /*****************************************************************************
650  * DeAllocatePicture:
651  * Deallocate a picture buffer and free all associated memory.
652  *****************************************************************************/
653 static void DeAllocatePicture( picture_t *p_pic, video_format_t *p_fmt )
654 {
655     if( p_fmt )
656     {
657         free( p_fmt->p_palette );
658         p_fmt->p_palette = NULL;
659     }
660
661     if( p_pic )
662         picture_Release( p_pic );
663 }
664
665 /*****************************************************************************
666  * SetOverlayTransparency: Set the transparency for this overlay fb,
667  * - true is make transparent
668  * - false is make non tranparent
669  *****************************************************************************/
670 static void SetOverlayTransparency( intf_thread_t *p_intf,
671                                     bool b_transparent )
672 {
673     intf_sys_t *p_sys = p_intf->p_sys;
674     size_t i_size = p_sys->fmt_out.i_width * p_sys->fmt_out.i_height
675                         * p_sys->i_bytes_per_pixel;
676     size_t i_page_size = (p_sys->i_page_size > i_size) ?
677                             i_size : p_sys->i_page_size;
678
679     if( p_sys->p_overlay )
680     {
681         msg_Dbg( p_intf, "Make overlay %s",
682                  b_transparent ? "transparent" : "opaque" );
683         if( b_transparent )
684             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, i_page_size );
685         else
686             memset( p_sys->p_overlay->p[0].p_pixels, 0x00, i_page_size );
687     }
688 }
689
690 #if defined(FBOSD_BLENDING)
691 /*****************************************************************************
692  * BlendPicture: Blend two pictures together..
693  *****************************************************************************/
694 static int BlendPicture( intf_thread_t *p_intf, video_format_t *p_fmt_src,
695                          video_format_t *p_fmt_dst, picture_t *p_pic_src,
696                          picture_t *p_pic_dst )
697 {
698     intf_sys_t *p_sys = p_intf->p_sys;
699     if( p_sys->p_blend && p_sys->p_blend->p_module )
700     {
701         int i_x_offset = p_sys->i_x;
702         int i_y_offset = p_sys->i_y;
703
704         memcpy( &p_sys->p_blend->fmt_in.video, p_fmt_src, sizeof( video_format_t ) );
705
706         /* Update the output picture size */
707         p_sys->p_blend->fmt_out.video.i_width =
708             p_sys->p_blend->fmt_out.video.i_visible_width =
709                 p_fmt_dst->i_width;
710         p_sys->p_blend->fmt_out.video.i_height =
711             p_sys->p_blend->fmt_out.video.i_visible_height =
712                 p_fmt_dst->i_height;
713
714         i_x_offset = __MAX( i_x_offset, 0 );
715         i_y_offset = __MAX( i_y_offset, 0 );
716
717         p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_pic_dst,
718             p_pic_src, p_pic_dst, i_x_offset, i_y_offset,
719             p_sys->i_alpha );
720
721         return VLC_SUCCESS;
722     }
723     return VLC_EGENERIC;
724 }
725
726 static int InvertAlpha( intf_thread_t *p_intf, picture_t **p_pic, video_format_t fmt )
727 {
728     uint8_t *p_begin = NULL, *p_end = NULL;
729     uint8_t i_skip = 0;
730
731     if( *p_pic && ((*p_pic)->i_planes != 1) )
732     {
733         msg_Err( p_intf,
734                  "cannot invert alpha channel too many planes %d (only 1 supported)",
735                  (*p_pic)->i_planes );
736         return VLC_EGENERIC;
737     }
738
739     switch( fmt.i_chroma )
740     {
741         case VLC_CODEC_RGB24:
742             p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
743             p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
744                       ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
745             i_skip = 3;
746             break;
747         case VLC_CODEC_RGB32:
748             p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
749             p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
750                       ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
751             i_skip = 4;
752             break;
753         default:
754             msg_Err( p_intf, "cannot invert alpha channel chroma not supported %4.4s",
755                     (char *)&fmt.i_chroma );
756             return VLC_EGENERIC;
757     }
758
759     for( ; p_begin < p_end; p_begin += i_skip )
760     {
761         uint8_t i_opacity = 0;
762
763         if( *p_begin != 0xFF )
764             i_opacity = 255 - *p_begin;
765         *p_begin = i_opacity;
766     }
767     /* end of kludge */
768     return VLC_SUCCESS;
769 }
770 #endif
771
772 /*****************************************************************************
773  * RenderPicture: Render the picture into the p_dest buffer.
774  * We don't take transparent pixels into account, so we don't have to blend
775  * the two images together.
776  *****************************************************************************/
777 static int RenderPicture( intf_thread_t *p_intf, int i_x_offset, int i_y_offset,
778                           picture_t *p_src, picture_t *p_dest )
779 {
780     int i;
781     VLC_UNUSED( p_intf );
782
783     if( !p_dest && !p_src ) return VLC_EGENERIC;
784
785     for( i = 0; i < p_src->i_planes ; i++ )
786     {
787         if( p_src->p[i].i_pitch == p_dest->p[i].i_pitch )
788         {
789             /* There are margins, but with the same width : perfect ! */
790             vlc_memcpy( p_dest->p[i].p_pixels, p_src->p[i].p_pixels,
791                         p_src->p[i].i_pitch * p_src->p[i].i_visible_lines );
792         }
793         else
794         {
795             /* We need to proceed line by line */
796             uint8_t *p_in  = p_src->p[i].p_pixels;
797             uint8_t *p_out = p_dest->p[i].p_pixels;
798
799             int i_x = i_x_offset * p_src->p[i].i_pixel_pitch;
800             int i_x_clip, i_y_clip;
801
802             /* Check boundaries, clip the image if necessary */
803             i_x_clip = ( i_x + p_src->p[i].i_visible_pitch ) - p_dest->p[i].i_visible_pitch;
804             i_x_clip = ( i_x_clip > 0 ) ? i_x_clip : 0;
805
806             i_y_clip = ( i_y_offset + p_src->p[i].i_visible_lines ) - p_dest->p[i].i_visible_lines;
807             i_y_clip = ( i_y_clip > 0 ) ? i_y_clip : 0;
808 #if defined(FBOSD_DEBUG)
809             msg_Dbg( p_intf, "i_pitch (%d,%d), (%d,%d)/(%d,%d)",
810                      p_dest->p[i].i_visible_pitch, p_src->p[i].i_visible_pitch,
811                      i_x_offset, i_y_offset, i_x, i_x_clip );
812 #endif
813             if( ( i_y_offset <= p_dest->p[i].i_visible_lines ) &&
814                 ( i_x <= p_dest->p[i].i_visible_pitch ) )
815             {
816                 int i_line;
817
818                 p_out += ( i_y_offset * p_dest->p[i].i_pitch );
819                 for( i_line = 0; i_line < ( p_src->p[i].i_visible_lines - i_y_clip ); i_line++ )
820                 {
821                     vlc_memcpy( p_out + i_x, p_in,
822                                 p_src->p[i].i_visible_pitch - i_x_clip );
823                     p_in += p_src->p[i].i_pitch;
824                     p_out += p_dest->p[i].i_pitch;
825                 }
826             }
827         }
828     }
829     return VLC_SUCCESS;
830 }
831
832 /*****************************************************************************
833  * RenderText - Render text to the desired picture format
834  *****************************************************************************/
835 static picture_t *RenderText( intf_thread_t *p_intf, const char *psz_string,
836                               text_style_t *p_style, video_format_t *p_fmt )
837 {
838     intf_sys_t *p_sys = p_intf->p_sys;
839     subpicture_region_t *p_region;
840     picture_t *p_dest = NULL;
841
842     if( !psz_string ) return p_dest;
843
844     if( p_sys->p_text && p_sys->p_text->p_module )
845     {
846         video_format_t fmt;
847
848         memset( &fmt, 0, sizeof(fmt) );
849         fmt.i_chroma = VLC_CODEC_TEXT;
850         fmt.i_aspect = 0;
851         fmt.i_width  = fmt.i_visible_width = 0;
852         fmt.i_height = fmt.i_visible_height = 0;
853         fmt.i_x_offset = 0;
854         fmt.i_y_offset = 0;
855
856         p_region = subpicture_region_New( &fmt );
857         if( !p_region )
858             return p_dest;
859
860         p_region->psz_text = strdup( psz_string );
861         if( !p_region->psz_text )
862         {
863             subpicture_region_Delete( p_region );
864             return NULL;
865         }
866         p_region->p_style = text_style_Duplicate( p_style );
867         p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
868
869         if( p_sys->p_text->pf_render_text )
870         {
871             video_format_t fmt_out;
872
873             memset( &fmt_out, 0, sizeof(video_format_t) );
874
875             p_sys->p_text->pf_render_text( p_sys->p_text,
876                                            p_region, p_region );
877
878 #if defined(FBOSD_BLENDING)
879             fmt_out = p_region->fmt;
880             fmt_out.i_bits_per_pixel = 32;
881             vlc_memcpy( p_fmt, &fmt_out, sizeof(video_format_t) );
882
883             /* FIXME not needed to copy the picture anymore no ? */
884             p_dest = AllocatePicture( VLC_OBJECT(p_intf), &fmt_out );
885             if( !p_dest )
886             {
887                 subpicture_region_Delete( p_region );
888                 return NULL;
889             }
890             picture_Copy( p_dest, p_region->p_picture );
891 #else
892             fmt_out.i_chroma = p_fmt->i_chroma;
893             p_dest = ConvertImage( p_intf, p_region->p_picture,
894                                    &p_region->fmt, &fmt_out );
895 #endif
896             subpicture_region_Delete( p_region );
897             return p_dest;
898         }
899         subpicture_region_Delete( p_region );
900     }
901     return p_dest;
902 }
903
904 /*****************************************************************************
905  * LoadImage: Load an image from file into a picture buffer.
906  *****************************************************************************/
907 static picture_t *LoadImage( intf_thread_t *p_intf, video_format_t *p_fmt,
908                              char *psz_file )
909 {
910     picture_t  *p_pic = NULL;
911
912     if( psz_file && p_intf->p_sys->p_image )
913     {
914         video_format_t fmt_in, fmt_out;
915
916         memset( &fmt_in, 0, sizeof(fmt_in) );
917         memset( &fmt_out, 0, sizeof(fmt_out) );
918
919         fmt_out.i_chroma = p_fmt->i_chroma;
920         p_pic = image_ReadUrl( p_intf->p_sys->p_image, psz_file,
921                                &fmt_in, &fmt_out );
922
923         msg_Dbg( p_intf, "image size %dx%d chroma %4.4s",
924                  fmt_out.i_width, fmt_out.i_height,
925                  (char *)&p_fmt->i_chroma );
926     }
927     return p_pic;
928 }
929
930 #if ! defined(FBOSD_BLENDING)
931 /*****************************************************************************
932  * Convertmage: Convert image to another fourcc
933  *****************************************************************************/
934 static picture_t *ConvertImage( intf_thread_t *p_intf, picture_t *p_pic,
935                          video_format_t *p_fmt_in, video_format_t *p_fmt_out )
936 {
937     intf_sys_t *p_sys = p_intf->p_sys;
938     picture_t  *p_old = NULL;
939
940     if( p_sys->p_image )
941     {
942         p_old = image_Convert( p_sys->p_image, p_pic, p_fmt_in, p_fmt_out );
943
944         msg_Dbg( p_intf, "converted image size %dx%d chroma %4.4s",
945                  p_fmt_out->i_width, p_fmt_out->i_height,
946                  (char *)&p_fmt_out->i_chroma );
947     }
948     return p_old;
949 }
950 #endif
951
952 /*****************************************************************************
953  * Init: initialize framebuffer video thread output method
954  *****************************************************************************/
955 static int Init( intf_thread_t *p_intf )
956 {
957     intf_sys_t *p_sys = p_intf->p_sys;
958
959     /* Initialize the output structure: RGB with square pixels, whatever
960      * the input format is, since it's the only format we know */
961     switch( p_sys->var_info.bits_per_pixel )
962     {
963     case 8: /* FIXME: set the palette */
964         p_sys->fmt_out.i_chroma = VLC_CODEC_RGB8; break;
965     case 15:
966         p_sys->fmt_out.i_chroma = VLC_CODEC_RGB15; break;
967     case 16:
968         p_sys->fmt_out.i_chroma = VLC_CODEC_RGB16; break;
969     case 24:
970         p_sys->fmt_out.i_chroma = VLC_CODEC_RGB24; break;
971     case 32:
972         p_sys->fmt_out.i_chroma = VLC_CODEC_RGB32; break;
973     default:
974         msg_Err( p_intf, "unknown screen depth %i",
975                  p_sys->var_info.bits_per_pixel );
976         return VLC_EGENERIC;
977     }
978
979     p_sys->fmt_out.i_bits_per_pixel = p_sys->var_info.bits_per_pixel;
980     p_sys->fmt_out.i_width  = p_sys->i_width;
981     p_sys->fmt_out.i_height = p_sys->i_height;
982
983     /* Assume we have square pixels */
984     if( p_sys->i_aspect < 0 )
985     {
986         p_sys->fmt_out.i_aspect = ( p_sys->i_width
987                                   * VOUT_ASPECT_FACTOR ) / p_sys->i_height;
988     }
989     else p_sys->fmt_out.i_aspect = p_sys->i_aspect;
990
991     p_sys->fmt_out.i_sar_num = p_sys->fmt_out.i_sar_den = 1;
992
993     /* Allocate overlay buffer */
994     p_sys->p_overlay = AllocatePicture( &p_sys->fmt_out );
995     if( !p_sys->p_overlay ) return VLC_EGENERIC;
996
997     SetOverlayTransparency( p_intf, true );
998
999     /* We know the chroma, allocate a buffer which will be used
1000      * to write to the overlay framebuffer */
1001     p_sys->p_overlay->p->i_pixel_pitch = p_sys->i_bytes_per_pixel;
1002     p_sys->p_overlay->p->i_lines = p_sys->var_info.yres;
1003     p_sys->p_overlay->p->i_visible_lines = p_sys->var_info.yres;
1004
1005     if( p_sys->var_info.xres_virtual )
1006     {
1007         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres_virtual
1008                              * p_sys->i_bytes_per_pixel;
1009     }
1010     else
1011     {
1012         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres
1013                              * p_sys->i_bytes_per_pixel;
1014     }
1015
1016     p_sys->p_overlay->p->i_visible_pitch = p_sys->var_info.xres
1017                                  * p_sys->i_bytes_per_pixel;
1018
1019     p_sys->p_overlay->i_planes = 1;
1020
1021     return VLC_SUCCESS;
1022 }
1023
1024 /*****************************************************************************
1025  * End: terminate framebuffer interface
1026  *****************************************************************************/
1027 static void End( intf_thread_t *p_intf )
1028 {
1029     intf_sys_t *p_sys = p_intf->p_sys;
1030
1031     /* CleanUp */
1032     SetOverlayTransparency( p_intf, false );
1033     if( p_sys->p_overlay )
1034     {
1035         int ret;
1036         ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1037                      p_sys->i_page_size );
1038         if( ret < 0 )
1039             msg_Err( p_intf, "unable to clear overlay" );
1040     }
1041
1042     DeAllocatePicture( p_intf->p_sys->p_overlay,
1043                        &p_intf->p_sys->fmt_out );
1044     p_intf->p_sys->p_overlay = NULL;
1045 }
1046
1047 /*****************************************************************************
1048  * OpenDisplay: initialize framebuffer
1049  *****************************************************************************/
1050 static int OpenDisplay( intf_thread_t *p_intf )
1051 {
1052     intf_sys_t *p_sys = p_intf->p_sys;
1053     char *psz_device;                             /* framebuffer device path */
1054     struct fb_fix_screeninfo    fix_info;     /* framebuffer fix information */
1055
1056     /* Open framebuffer device */
1057     if( !(psz_device = config_GetPsz( p_intf, "fbosd-dev" )) )
1058     {
1059         msg_Err( p_intf, "don't know which fb osd/overlay device to open" );
1060         return VLC_EGENERIC;
1061     }
1062
1063     p_sys->i_fd = utf8_open( psz_device, O_RDWR );
1064     if( p_sys->i_fd == -1 )
1065     {
1066         msg_Err( p_intf, "cannot open %s (%m)", psz_device );
1067         free( psz_device );
1068         return VLC_EGENERIC;
1069     }
1070     free( psz_device );
1071
1072     /* Get framebuffer device information */
1073     if( ioctl( p_sys->i_fd, FBIOGET_VSCREENINFO, &p_sys->var_info ) )
1074     {
1075         msg_Err( p_intf, "cannot get fb info (%m)" );
1076         close( p_sys->i_fd );
1077         return VLC_EGENERIC;
1078     }
1079
1080     /* Get some info on the framebuffer itself */
1081     if( ioctl( p_sys->i_fd, FBIOGET_FSCREENINFO, &fix_info ) == 0 )
1082     {
1083         p_sys->i_width = p_sys->fmt_out.i_width = p_sys->var_info.xres;
1084         p_sys->i_height = p_sys->fmt_out.i_height = p_sys->var_info.yres;
1085     }
1086
1087     /* FIXME: if the image is full-size, it gets cropped on the left
1088      * because of the xres / xres_virtual slight difference */
1089     msg_Dbg( p_intf, "%ix%i (virtual %ix%i)",
1090              p_sys->var_info.xres, p_sys->var_info.yres,
1091              p_sys->var_info.xres_virtual,
1092              p_sys->var_info.yres_virtual );
1093
1094     p_sys->fmt_out.i_width = p_sys->i_width;
1095     p_sys->fmt_out.i_height = p_sys->i_height;
1096
1097     p_sys->p_palette = NULL;
1098     p_sys->b_pan = ( fix_info.ypanstep || fix_info.ywrapstep );
1099
1100     switch( p_sys->var_info.bits_per_pixel )
1101     {
1102     case 8:
1103         p_sys->p_palette = malloc( 8 * 256 * sizeof( uint16_t ) );
1104         if( !p_sys->p_palette )
1105         {
1106             close( p_sys->i_fd );
1107             return VLC_ENOMEM;
1108         }
1109         p_sys->fb_cmap.start = 0;
1110         p_sys->fb_cmap.len = 256;
1111         p_sys->fb_cmap.red = p_sys->p_palette;
1112         p_sys->fb_cmap.green  = p_sys->p_palette + 256 * sizeof( uint16_t );
1113         p_sys->fb_cmap.blue   = p_sys->p_palette + 2 * 256 * sizeof( uint16_t );
1114         p_sys->fb_cmap.transp = p_sys->p_palette + 3 * 256 * sizeof( uint16_t );
1115
1116         /* Save the colormap */
1117         ioctl( p_sys->i_fd, FBIOGETCMAP, &p_sys->fb_cmap );
1118
1119         p_sys->i_bytes_per_pixel = 1;
1120         break;
1121
1122     case 15:
1123     case 16:
1124         p_sys->i_bytes_per_pixel = 2;
1125         break;
1126
1127     case 24:
1128         p_sys->i_bytes_per_pixel = 3;
1129         break;
1130
1131     case 32:
1132         p_sys->i_bytes_per_pixel = 4;
1133         break;
1134
1135     default:
1136         msg_Err( p_intf, "screen depth %d is not supported",
1137                          p_sys->var_info.bits_per_pixel );
1138
1139         close( p_sys->i_fd );
1140         return VLC_EGENERIC;
1141     }
1142
1143     p_sys->i_page_size = p_sys->i_width * p_sys->i_height
1144                          * p_sys->i_bytes_per_pixel;
1145
1146     msg_Dbg( p_intf, "framebuffer type=%d, visual=%d, ypanstep=%d, "
1147              "ywrap=%d, accel=%d", fix_info.type, fix_info.visual,
1148              fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
1149     return VLC_SUCCESS;
1150 }
1151
1152 /*****************************************************************************
1153  * CloseDisplay: terminate FB interface thread
1154  *****************************************************************************/
1155 static void CloseDisplay( intf_thread_t *p_intf )
1156 {
1157     intf_sys_t *p_sys = p_intf->p_sys;
1158
1159     /* Restore palette */
1160     if( p_sys->var_info.bits_per_pixel == 8 )
1161     {
1162         ioctl( p_sys->i_fd, FBIOPUTCMAP, &p_sys->fb_cmap );
1163         free( p_sys->p_palette );
1164         p_sys->p_palette = NULL;
1165     }
1166
1167     /* Close fb */
1168     close( p_sys->i_fd );
1169 }
1170
1171 static void Render( intf_thread_t *p_intf, struct fbosd_render_t *render )
1172 {
1173     intf_sys_t *p_sys = p_intf->p_sys;
1174
1175     if( render->i_state != FBOSD_STATE_RENDER ) return;
1176     if( !render->psz_string ) return;
1177
1178     if( render->i_type == FBOSD_RENDER_IMAGE )
1179     {
1180         picture_t *p_pic;
1181         p_pic = LoadImage( p_intf, &p_sys->fmt_out, render->psz_string );
1182         if( p_pic )
1183         {
1184             RenderPicture( p_intf, render->i_x, render->i_y,
1185                            p_pic, p_sys->p_overlay );
1186             picture_Release( p_pic );
1187         }
1188     }
1189     else if( render->i_type == FBOSD_RENDER_TEXT )
1190     {
1191         picture_t *p_text;
1192 #if defined(FBOSD_BLENDING)
1193         video_format_t fmt_in;
1194         memset( &fmt_in, 0, sizeof(video_format_t) );
1195         p_text = RenderText( p_intf, render->psz_string, render->p_text_style,
1196                              &fmt_in );
1197         if( p_text )
1198         {
1199             BlendPicture( p_intf, &fmt_in, &p_sys->fmt_out,
1200                           p_text, p_sys->p_overlay );
1201             msg_Dbg( p_intf, "releasing picture" );
1202             DeAllocatePicture( p_text, &fmt_in );
1203         }
1204 #else
1205         p_text = RenderText( p_intf, render->psz_string, render->p_text_style,
1206                              &p_sys->fmt_out );
1207         if( p_text )
1208         {
1209             RenderPicture( p_intf, render->i_x, render->i_y,
1210                            p_text, p_sys->p_overlay );
1211             picture_Release( p_text );
1212         }
1213 #endif
1214     }
1215 }
1216
1217 static void RenderClear( intf_thread_t *p_intf, struct fbosd_render_t *render )
1218 {
1219     intf_sys_t *p_sys = p_intf->p_sys;
1220
1221     text_style_Delete( render->p_text_style );
1222     render->p_text_style = text_style_New();
1223     free( render->psz_string );
1224     render->psz_string = NULL;
1225
1226     render->i_x = p_sys->i_x;
1227     render->i_y = p_sys->i_y;
1228     render->i_pos = p_sys->i_pos;
1229     render->i_alpha = p_sys->i_alpha;
1230     render->b_absolute = p_sys->b_absolute;
1231     render->i_state = FBOSD_STATE_FREE;
1232 }
1233
1234 static bool isRendererReady( intf_thread_t *p_intf )
1235 {
1236     intf_sys_t *p_sys = p_intf->p_sys;
1237     int i;
1238
1239     /* Check if there are more items to render */
1240     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1241     {
1242         if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1243             return false;
1244     }
1245     return true;
1246 }
1247
1248 /*****************************************************************************
1249  * Run: thread
1250  *****************************************************************************
1251  * This part of the interface is in a separate thread so that we can call
1252  * exec() from within it without annoying the rest of the program.
1253  *****************************************************************************/
1254 static void Run( intf_thread_t *p_intf )
1255 {
1256     intf_sys_t *p_sys = p_intf->p_sys;
1257     int canc = vlc_savecancel();
1258
1259     while( vlc_object_alive( p_intf ) )
1260     {
1261         int i;
1262
1263         /* Is there somthing to render? */
1264         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1265         {
1266             if( p_sys->render[i].i_state == FBOSD_STATE_RENDER )
1267             {
1268                 Render( p_intf, &p_sys->render[i] );
1269                 RenderClear( p_intf, &p_sys->render[i] );
1270             }
1271         }
1272
1273         if( p_sys->b_clear )
1274         {
1275             SetOverlayTransparency( p_intf, true );
1276
1277             var_SetString( p_intf, "fbosd-image", "" );
1278             var_SetString( p_intf, "fbosd-text", "" );
1279
1280             p_sys->b_clear = false;
1281             p_sys->b_need_update = true;
1282         }
1283
1284         if( p_sys->b_need_update && p_sys->p_overlay &&
1285             isRendererReady( p_intf ) )
1286         {
1287             int ret;
1288 #if defined(FBOSD_BLENDING)
1289             /* Reverse alpha channel to work around FPGA bug */
1290             InvertAlpha( p_intf, &p_sys->p_overlay, p_sys->fmt_out );
1291 #endif
1292             ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1293                          p_sys->i_page_size );
1294             if( ret < 0 )
1295                 msg_Err( p_intf, "unable to write to overlay" );
1296             lseek( p_sys->i_fd, 0, SEEK_SET );
1297
1298             /* clear the picture */
1299             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, p_sys->i_page_size );
1300             p_sys->b_need_update = false;
1301         }
1302
1303         msleep( INTF_IDLE_SLEEP );
1304     }
1305
1306     End( p_intf );
1307     vlc_restorecancel( canc );
1308 }
1309
1310 static int OverlayCallback( vlc_object_t *p_this, char const *psz_cmd,
1311                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1312 {
1313     intf_thread_t *p_intf = (intf_thread_t *) p_this;
1314     intf_sys_t *p_sys = p_intf->p_sys;
1315     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1316
1317     if( !strncmp( psz_cmd, "fbosd-display", 13 ) )
1318     {
1319         p_sys->b_need_update = true;
1320     }
1321     else if( !strncmp( psz_cmd, "fbosd-clear", 11 ) )
1322     {
1323         int i;
1324         /* Clear the entire render list */
1325         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1326         {
1327             RenderClear( p_intf, &p_sys->render[i] );
1328         }
1329         p_sys->b_clear = true;
1330     }
1331     else if( !strncmp( psz_cmd, "fbosd-render", 12 ) )
1332     {
1333         int i;
1334         /* Are we already busy with on slot ? */
1335         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1336         {
1337             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1338             {
1339                 p_sys->render[i].i_state = FBOSD_STATE_RENDER;
1340                 break;
1341             }
1342         }
1343     }
1344     else
1345     {
1346         int i;
1347         /* Are we already busy with on slot ? */
1348         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1349         {
1350             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1351                 break;
1352         }
1353         /* No, then find first FREE slot */
1354         if( p_sys->render[i].i_state != FBOSD_STATE_RESERVED )
1355         {
1356             for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1357             {
1358                 if( p_sys->render[i].i_state == FBOSD_STATE_FREE )
1359                     break;
1360             }
1361             if( p_sys->render[i].i_state != FBOSD_STATE_FREE )
1362             {
1363                 msg_Warn( p_this, "render space depleated" );
1364                 return VLC_SUCCESS;
1365             }
1366         }
1367         /* Found a free slot */
1368         p_sys->render[i].i_state = FBOSD_STATE_RESERVED;
1369         if( !strncmp( psz_cmd, "fbosd-image", 11 ) )
1370         {
1371             free( p_sys->render[i].psz_string );
1372             p_sys->render[i].psz_string = strdup( newval.psz_string );
1373             p_sys->render[i].i_type = FBOSD_RENDER_IMAGE;
1374         }
1375         else if( !strncmp( psz_cmd, "fbosd-text", 10 ) )
1376         {
1377             free( p_sys->render[i].psz_string );
1378             p_sys->render[i].psz_string = strdup( newval.psz_string );
1379             p_sys->render[i].i_type = FBOSD_RENDER_TEXT;
1380         }
1381         else if( !strncmp( psz_cmd, "fbosd-x", 7 ) )
1382         {
1383             p_sys->render[i].b_absolute = false;
1384             p_sys->render[i].i_x = (newval.i_int < p_sys->i_width) ?
1385                                     newval.i_int : p_sys->i_width;
1386         }
1387         else if( !strncmp( psz_cmd, "fbosd-y", 7 ) )
1388         {
1389             p_sys->render[i].b_absolute = false;
1390             p_sys->render[i].i_y = (newval.i_int < p_sys->i_height) ?
1391                                     newval.i_int : p_sys->i_height;
1392         }
1393         else if( !strncmp( psz_cmd, "fbosd-position", 14 ) )
1394         {
1395             p_sys->render[i].b_absolute = true;
1396             p_sys->render[i].i_pos = newval.i_int;
1397         }
1398         else if( !strncmp( psz_cmd, "fbosd-font-size", 15 ) )
1399         {
1400             p_sys->render[i].p_text_style->i_font_size = newval.i_int;
1401         }
1402         else if( !strncmp( psz_cmd, "fbosd-font-color", 16 ) )
1403         {
1404             p_sys->render[i].p_text_style->i_font_color = newval.i_int;
1405         }
1406         else if( !strncmp( psz_cmd, "fbosd-font-opacity", 18 ) )
1407         {
1408             p_sys->render[i].p_text_style->i_font_alpha = 255 - newval.i_int;
1409         }
1410         else if( !strncmp( psz_cmd, "fbosd-alpha", 11 ) )
1411         {
1412             p_sys->render[i].i_alpha = newval.i_int;
1413         }
1414     }
1415     return VLC_SUCCESS;
1416 }