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