4c1f65768496150503ae77ce888d31c973ed3973
[vlc.git] / 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/fb1", 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 = malloc( sizeof( intf_sys_t ) );
310     if( !p_intf->p_sys )
311         return VLC_ENOMEM;
312     memset( p_sys, 0, sizeof(intf_sys_t) );
313
314     p_sys->p_style = malloc( sizeof( text_style_t ) );
315     if( !p_sys->p_style )
316     {
317         free( p_intf->p_sys );
318         return VLC_ENOMEM;
319     }
320     vlc_memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ) );
321
322     p_intf->pf_run = Run;
323
324     p_sys->p_image = image_HandlerCreate( p_this );
325     if( !p_sys->p_image )
326     {
327         free( p_intf->p_sys->p_style );
328         free( p_intf->p_sys );
329         return VLC_ENOMEM;
330     }
331
332     p_sys->i_alpha = var_CreateGetIntegerCommand( p_intf, "fbosd-alpha" );
333     var_AddCallback( p_intf, "fbosd-alpha", OverlayCallback, NULL );
334
335     p_sys->i_aspect = -1;
336     psz_aspect =
337             var_CreateGetNonEmptyString( p_intf, "fbosd-aspect-ratio" );
338     if( psz_aspect )
339     {
340         char *psz_parser = strchr( psz_aspect, ':' );
341
342         if( psz_parser )
343         {
344             *psz_parser++ = '\0';
345             p_sys->i_aspect = ( atoi( psz_aspect )
346                               * VOUT_ASPECT_FACTOR ) / atoi( psz_parser );
347             p_sys->fmt_out.i_aspect = p_sys->i_aspect;
348         }
349         msg_Dbg( p_intf, "using aspect ratio %d:%d",
350                   atoi( psz_aspect ), atoi( psz_parser ) );
351
352         free( psz_aspect );
353         psz_aspect = NULL;
354     }
355
356     /* Use PAL by default */
357     p_sys->i_width  = p_sys->fmt_out.i_width  = 704;
358     p_sys->i_height = p_sys->fmt_out.i_height = 576;
359
360     psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-image" );
361     var_AddCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
362     if( psz_tmp && *psz_tmp )
363     {
364         p_sys->render[0].i_type = FBOSD_RENDER_IMAGE;
365         p_sys->render[0].i_state = FBOSD_STATE_RENDER;
366         p_sys->render[0].psz_string = strdup( psz_tmp );
367     }
368     free( psz_tmp );
369
370     psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-text" );
371     var_AddCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
372     if( psz_tmp && *psz_tmp )
373     {
374         p_sys->render[1].i_type = FBOSD_RENDER_TEXT;
375         p_sys->render[1].i_state = FBOSD_STATE_RENDER;
376         p_sys->render[1].psz_string = strdup( psz_tmp );
377     }
378     free( psz_tmp );
379
380     p_sys->i_pos = var_CreateGetIntegerCommand( p_intf, "fbosd-position" );
381     p_sys->i_x = var_CreateGetIntegerCommand( p_intf, "fbosd-x" );
382     p_sys->i_y = var_CreateGetIntegerCommand( p_intf, "fbosd-y" );
383
384     var_AddCallback( p_intf, "fbosd-position", OverlayCallback, NULL );
385     var_AddCallback( p_intf, "fbosd-x", OverlayCallback, NULL );
386     var_AddCallback( p_intf, "fbosd-y", OverlayCallback, NULL );
387
388     p_sys->p_style->i_font_size =
389             var_CreateGetIntegerCommand( p_intf, "fbosd-font-size" );
390     p_sys->p_style->i_font_color =
391             var_CreateGetIntegerCommand( p_intf, "fbosd-font-color" );
392     p_sys->p_style->i_font_alpha = 255 -
393             var_CreateGetIntegerCommand( p_intf, "fbosd-font-opacity" );
394
395     var_AddCallback( p_intf, "fbosd-font-color", OverlayCallback, NULL );
396     var_AddCallback( p_intf, "fbosd-font-size", OverlayCallback, NULL );
397     var_AddCallback( p_intf, "fbosd-font-opacity", OverlayCallback, NULL );
398
399     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
400     {
401         vlc_memcpy( &p_sys->render[i].text_style, &default_text_style,
402                     sizeof( text_style_t ) );
403     }
404
405     p_sys->b_clear = var_CreateGetBoolCommand( p_intf, "fbosd-clear" );
406     p_sys->b_render = var_CreateGetBoolCommand( p_intf, "fbosd-render" );
407     p_sys->b_need_update = var_CreateGetBoolCommand( p_intf, "fbosd-display" );
408
409     var_AddCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
410     var_AddCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
411     var_AddCallback( p_intf, "fbosd-display", OverlayCallback, NULL );
412
413     /* Check if picture position was overridden */
414     p_sys->b_absolute = true;
415     if( ( p_sys->i_x >= 0 ) && ( p_sys->i_y >= 0 ) )
416     {
417         p_sys->b_absolute = false;
418         p_sys->i_y = (p_sys->i_y < p_sys->i_height) ?
419                         p_sys->i_y : p_sys->i_height;
420         p_sys->i_x = (p_sys->i_x < p_sys->i_width) ?
421                         p_sys->i_x : p_sys->i_width;
422     }
423
424     p_sys->render[0].i_x = p_sys->render[1].i_x = p_sys->i_x;
425     p_sys->render[0].i_y = p_sys->render[1].i_y = p_sys->i_y;
426     p_sys->render[0].i_pos = p_sys->render[1].i_pos = p_sys->i_pos;
427     p_sys->render[0].i_alpha = p_sys->render[1].i_alpha = p_sys->i_alpha;
428
429     /* Initialize framebuffer */
430     if( OpenDisplay( p_intf ) )
431     {
432         Destroy( VLC_OBJECT(p_intf) );
433         return VLC_EGENERIC;
434     }
435
436     Init( p_intf );
437
438 #if defined(FBOSD_BLENDING)
439     /* Load the blending module */
440     if( OpenBlending( p_intf ) )
441     {
442         msg_Err( p_intf, "Unable to load image blending module" );
443         Destroy( VLC_OBJECT(p_intf) );
444         return VLC_EGENERIC;
445     }
446 #endif
447
448     /* Load text renderer module */
449     if( OpenTextRenderer( p_intf ) )
450     {
451         msg_Err( p_intf, "Unable to load text rendering module" );
452         Destroy( VLC_OBJECT(p_intf) );
453         return VLC_EGENERIC;
454     }
455
456     p_sys->b_render = true;
457     p_sys->b_need_update = true;
458
459     return VLC_SUCCESS;
460 }
461
462 /*****************************************************************************
463  * Destroy: destroy FB interface thread output method
464  *****************************************************************************
465  * Terminate an output method created by Create
466  *****************************************************************************/
467 static void Destroy( vlc_object_t *p_this )
468 {
469     intf_thread_t *p_intf = (intf_thread_t *)p_this;
470     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
471     int i;
472
473     p_sys->b_need_update = false;
474     p_sys->b_render = false;
475     p_sys->b_clear = false;
476
477     var_DelCallback( p_intf, "fbosd-alpha", OverlayCallback, NULL );
478     var_Destroy( p_intf, "fbosd-alpha" );
479
480     var_DelCallback( p_intf, "fbosd-x", OverlayCallback, NULL );
481     var_DelCallback( p_intf, "fbosd-y", OverlayCallback, NULL );
482     var_DelCallback( p_intf, "fbosd-position", OverlayCallback, NULL );
483     var_DelCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
484     var_DelCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
485     var_DelCallback( p_intf, "fbosd-font-size", OverlayCallback, NULL );
486     var_DelCallback( p_intf, "fbosd-font-color", OverlayCallback, NULL );
487     var_DelCallback( p_intf, "fbosd-font-opacity", OverlayCallback, NULL );
488     var_DelCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
489     var_DelCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
490     var_DelCallback( p_intf, "fbosd-display", OverlayCallback, NULL );
491
492     var_Destroy( p_intf, "fbosd-x" );
493     var_Destroy( p_intf, "fbosd-y" );
494     var_Destroy( p_intf, "fbosd-position" );
495     var_Destroy( p_intf, "fbosd-image" );
496     var_Destroy( p_intf, "fbosd-text" );
497     var_Destroy( p_intf, "fbosd-font-size" );
498     var_Destroy( p_intf, "fbosd-font-color" );
499     var_Destroy( p_intf, "fbosd-font-opacity" );
500     var_Destroy( p_intf, "fbosd-clear" );
501     var_Destroy( p_intf, "fbosd-render" );
502     var_Destroy( p_intf, "fbosd-display" );
503
504     var_Destroy( p_intf, "fbosd-aspect-ratio" );
505
506     CloseDisplay( p_intf );
507
508     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
509     {
510         free( p_sys->render[i].psz_string );
511         p_sys->render[i].i_state = FBOSD_STATE_FREE;
512     }
513
514 #if defined(FBOSD_BLENDING)
515     if( p_sys->p_blend ) CloseBlending( p_intf );
516 #endif
517     if( p_sys->p_text )  CloseTextRenderer( p_intf );
518
519     if( p_sys->p_image )
520         image_HandlerDelete( p_sys->p_image );
521     if( p_sys->p_overlay )
522         picture_Release( p_sys->p_overlay );
523
524     free( p_sys->p_style );
525     free( p_sys );
526 }
527
528 #if defined(FBOSD_BLENDING)
529 static int OpenBlending( intf_thread_t *p_intf )
530 {
531     if( p_intf->p_sys->p_blend ) return VLC_EGENERIC;
532
533     p_intf->p_sys->p_blend =
534             vlc_object_create( p_intf, sizeof(filter_t) );
535     vlc_object_attach( p_intf->p_sys->p_blend, p_intf );
536     p_intf->p_sys->p_blend->fmt_out.video.i_x_offset =
537         p_intf->p_sys->p_blend->fmt_out.video.i_y_offset = 0;
538     p_intf->p_sys->p_blend->fmt_out.video.i_aspect =
539             p_intf->p_sys->fmt_out.i_aspect;
540     p_intf->p_sys->p_blend->fmt_out.video.i_chroma =
541             p_intf->p_sys->fmt_out.i_chroma;
542     if( config_GetInt( p_intf, "freetype-yuvp" ) )
543         p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
544                 VLC_FOURCC('Y','U','V','P');
545     else
546         p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
547                 VLC_FOURCC('Y','U','V','A');
548
549     p_intf->p_sys->p_blend->p_module =
550         module_Need( p_intf->p_sys->p_blend, "video blending", 0, 0 );
551
552     if( !p_intf->p_sys->p_blend->p_module )
553         return VLC_EGENERIC;
554
555     return VLC_SUCCESS;
556 }
557
558 static void CloseBlending( intf_thread_t *p_intf )
559 {
560     if( p_intf->p_sys->p_blend )
561     {
562         if( p_intf->p_sys->p_blend->p_module )
563             module_Unneed( p_intf->p_sys->p_blend,
564                            p_intf->p_sys->p_blend->p_module );
565
566         vlc_object_detach( p_intf->p_sys->p_blend );
567         vlc_object_release( p_intf->p_sys->p_blend );
568     }
569 }
570 #endif
571
572 static int OpenTextRenderer( intf_thread_t *p_intf )
573 {
574     char *psz_modulename = NULL;
575
576     if( p_intf->p_sys->p_text ) return VLC_EGENERIC;
577
578     p_intf->p_sys->p_text =
579             vlc_object_create( p_intf, sizeof(filter_t) );
580     vlc_object_attach( p_intf->p_sys->p_text, p_intf );
581
582     p_intf->p_sys->p_text->fmt_out.video.i_width =
583         p_intf->p_sys->p_text->fmt_out.video.i_visible_width =
584         p_intf->p_sys->i_width;
585     p_intf->p_sys->p_text->fmt_out.video.i_height =
586         p_intf->p_sys->p_text->fmt_out.video.i_visible_height =
587         p_intf->p_sys->i_height;
588
589     psz_modulename = var_CreateGetString( p_intf, "text-renderer" );
590     if( psz_modulename && *psz_modulename )
591     {
592         p_intf->p_sys->p_text->p_module =
593             module_Need( p_intf->p_sys->p_text, "text renderer",
594                             psz_modulename, true );
595     }
596     if( !p_intf->p_sys->p_text->p_module )
597     {
598         p_intf->p_sys->p_text->p_module =
599             module_Need( p_intf->p_sys->p_text, "text renderer", 0, 0 );
600     }
601     free( psz_modulename );
602
603     if( !p_intf->p_sys->p_text->p_module )
604         return VLC_EGENERIC;
605
606     return VLC_SUCCESS;
607 }
608
609 static void CloseTextRenderer( intf_thread_t *p_intf )
610 {
611     if( p_intf->p_sys->p_text )
612     {
613         if( p_intf->p_sys->p_text->p_module )
614             module_Unneed( p_intf->p_sys->p_text,
615                            p_intf->p_sys->p_text->p_module );
616
617         vlc_object_detach( p_intf->p_sys->p_text );
618         vlc_object_release( p_intf->p_sys->p_text );
619     }
620 }
621
622 /*****************************************************************************
623  * AllocatePicture:
624  * allocate a picture buffer for use with the overlay fb.
625  *****************************************************************************/
626 static picture_t *AllocatePicture( video_format_t *p_fmt )
627 {
628     picture_t *p_picture = picture_New( p_fmt->i_chroma,
629                                         p_fmt->i_width, p_fmt->i_height,
630                                         p_fmt->i_aspect );
631     if( !p_picture )
632         return NULL;
633
634     if( !p_fmt->p_palette &&
635         ( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') ) )
636     {
637         p_fmt->p_palette = malloc( sizeof(video_palette_t) );
638         if( !p_fmt->p_palette )
639         {
640             picture_Release( p_picture );
641             return NULL;
642         }
643     }
644     else
645     {
646         p_fmt->p_palette = NULL;
647     }
648
649     return p_picture;
650 }
651
652 /*****************************************************************************
653  * DeAllocatePicture:
654  * Deallocate a picture buffer and free all associated memory.
655  *****************************************************************************/
656 static void DeAllocatePicture( picture_t *p_pic, video_format_t *p_fmt )
657 {
658     if( p_fmt )
659     {
660         free( p_fmt->p_palette );
661         p_fmt->p_palette = NULL;
662     }
663
664     if( p_pic )
665         picture_Release( p_pic );
666 }
667
668 /*****************************************************************************
669  * SetOverlayTransparency: Set the transparency for this overlay fb,
670  * - true is make transparent
671  * - false is make non tranparent
672  *****************************************************************************/
673 static void SetOverlayTransparency( intf_thread_t *p_intf,
674                                     bool b_transparent )
675 {
676     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
677     size_t i_size = p_sys->fmt_out.i_width * p_sys->fmt_out.i_height
678                         * p_sys->i_bytes_per_pixel;
679     size_t i_page_size = (p_sys->i_page_size > i_size) ?
680                             i_size : p_sys->i_page_size;
681
682     if( p_sys->p_overlay )
683     {
684         msg_Dbg( p_intf, "Make overlay %s",
685                  b_transparent ? "transparent" : "opaque" );
686         if( b_transparent )
687             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, i_page_size );
688         else
689             memset( p_sys->p_overlay->p[0].p_pixels, 0x00, i_page_size );
690     }
691 }
692
693 #if defined(FBOSD_BLENDING)
694 /*****************************************************************************
695  * BlendPicture: Blend two pictures together..
696  *****************************************************************************/
697 static int BlendPicture( intf_thread_t *p_intf, video_format_t *p_fmt_src,
698                          video_format_t *p_fmt_dst, picture_t *p_pic_src,
699                          picture_t *p_pic_dst )
700 {
701     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
702     if( p_sys->p_blend && p_sys->p_blend->p_module )
703     {
704         int i_x_offset = p_sys->i_x;
705         int i_y_offset = p_sys->i_y;
706
707         memcpy( &p_sys->p_blend->fmt_in.video, p_fmt_src, sizeof( video_format_t ) );
708
709         /* Update the output picture size */
710         p_sys->p_blend->fmt_out.video.i_width =
711             p_sys->p_blend->fmt_out.video.i_visible_width =
712                 p_fmt_dst->i_width;
713         p_sys->p_blend->fmt_out.video.i_height =
714             p_sys->p_blend->fmt_out.video.i_visible_height =
715                 p_fmt_dst->i_height;
716
717         i_x_offset = __MAX( i_x_offset, 0 );
718         i_y_offset = __MAX( i_y_offset, 0 );
719
720         p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_pic_dst,
721             p_pic_src, p_pic_dst, i_x_offset, i_y_offset,
722             p_sys->i_alpha );
723
724         return VLC_SUCCESS;
725     }
726     return VLC_EGENERIC;
727 }
728
729 static int InvertAlpha( intf_thread_t *p_intf, picture_t **p_pic, video_format_t fmt )
730 {
731     uint8_t *p_begin = NULL, *p_end = NULL;
732     uint8_t i_skip = 0;
733
734     if( *p_pic && ((*p_pic)->i_planes != 1) )
735     {
736         msg_Err( p_intf,
737                  "cannot invert alpha channel too many planes %d (only 1 supported)",
738                  (*p_pic)->i_planes );
739         return VLC_EGENERIC;
740     }
741
742     switch( fmt.i_chroma )
743     {
744         case VLC_FOURCC('R','V','2','4'):
745             p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
746             p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
747                       ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
748             i_skip = 3;
749             break;
750         case VLC_FOURCC('R','V','3','2'):
751             p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
752             p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
753                       ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
754             i_skip = 4;
755             break;
756         default:
757             msg_Err( p_intf, "cannot invert alpha channel chroma not supported %4.4s",
758                     (char *)&fmt.i_chroma );
759             return VLC_EGENERIC;
760     }
761
762     for( ; p_begin < p_end; p_begin += i_skip )
763     {
764         uint8_t i_opacity = 0;
765
766         if( *p_begin != 0xFF )
767             i_opacity = 255 - *p_begin;
768         *p_begin = i_opacity;
769     }
770     /* end of kludge */
771     return VLC_SUCCESS;
772 }
773 #endif
774
775 /*****************************************************************************
776  * RenderPicture: Render the picture into the p_dest buffer.
777  * We don't take transparent pixels into account, so we don't have to blend
778  * the two images together.
779  *****************************************************************************/
780 static int RenderPicture( intf_thread_t *p_intf, int i_x_offset, int i_y_offset,
781                           picture_t *p_src, picture_t *p_dest )
782 {
783     int i;
784     VLC_UNUSED( p_intf );
785
786     if( !p_dest && !p_src ) return VLC_EGENERIC;
787
788     for( i = 0; i < p_src->i_planes ; i++ )
789     {
790         if( p_src->p[i].i_pitch == p_dest->p[i].i_pitch )
791         {
792             /* There are margins, but with the same width : perfect ! */
793             vlc_memcpy( p_dest->p[i].p_pixels, p_src->p[i].p_pixels,
794                         p_src->p[i].i_pitch * p_src->p[i].i_visible_lines );
795         }
796         else
797         {
798             /* We need to proceed line by line */
799             uint8_t *p_in  = p_src->p[i].p_pixels;
800             uint8_t *p_out = p_dest->p[i].p_pixels;
801
802             int i_x = i_x_offset * p_src->p[i].i_pixel_pitch;
803             int i_x_clip, i_y_clip;
804
805             /* Check boundaries, clip the image if necessary */
806             i_x_clip = ( i_x + p_src->p[i].i_visible_pitch ) - p_dest->p[i].i_visible_pitch;
807             i_x_clip = ( i_x_clip > 0 ) ? i_x_clip : 0;
808
809             i_y_clip = ( i_y_offset + p_src->p[i].i_visible_lines ) - p_dest->p[i].i_visible_lines;
810             i_y_clip = ( i_y_clip > 0 ) ? i_y_clip : 0;
811 #if defined(FBOSD_DEBUG)
812             msg_Dbg( p_intf, "i_pitch (%d,%d), (%d,%d)/(%d,%d)",
813                      p_dest->p[i].i_visible_pitch, p_src->p[i].i_visible_pitch,
814                      i_x_offset, i_y_offset, i_x, i_x_clip );
815 #endif
816             if( ( i_y_offset <= p_dest->p[i].i_visible_lines ) &&
817                 ( i_x <= p_dest->p[i].i_visible_pitch ) )
818             {
819                 int i_line;
820
821                 p_out += ( i_y_offset * p_dest->p[i].i_pitch );
822                 for( i_line = 0; i_line < ( p_src->p[i].i_visible_lines - i_y_clip ); i_line++ )
823                 {
824                     vlc_memcpy( p_out + i_x, p_in,
825                                 p_src->p[i].i_visible_pitch - i_x_clip );
826                     p_in += p_src->p[i].i_pitch;
827                     p_out += p_dest->p[i].i_pitch;
828                 }
829             }
830         }
831     }
832     return VLC_SUCCESS;
833 }
834
835 /*****************************************************************************
836  * RenderText - Render text to the desired picture format
837  *****************************************************************************/
838 static picture_t *RenderText( intf_thread_t *p_intf, const char *psz_string,
839                               text_style_t *p_style, video_format_t *p_fmt )
840 {
841     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
842     subpicture_region_t *p_region;
843     picture_t *p_dest = NULL;
844
845     if( !psz_string ) return p_dest;
846
847     if( p_sys->p_text && p_sys->p_text->p_module )
848     {
849         p_region = (subpicture_region_t *) malloc( sizeof(subpicture_region_t) );
850         if( !p_region )
851             return p_dest;
852
853         memset( p_region, 0, sizeof(subpicture_region_t) );
854
855         p_region->psz_text = strdup( psz_string );
856         if( !p_region->psz_text )
857         {
858             free( p_region );
859             return NULL;
860         }
861         p_region->p_style = p_style;
862
863         p_region->fmt.i_chroma = VLC_FOURCC('T','E','X','T');
864         p_region->fmt.i_aspect = 0;
865         p_region->fmt.i_width = p_region->fmt.i_visible_width = 0;
866         p_region->fmt.i_height = p_region->fmt.i_visible_height = 0;
867         p_region->fmt.i_x_offset = 0;
868         p_region->fmt.i_y_offset = 0;
869
870         p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
871
872         if( p_sys->p_text->pf_render_text )
873         {
874             video_format_t fmt_out;
875
876             memset( &fmt_out, 0, sizeof(video_format_t) );
877
878             p_sys->p_text->pf_render_text( p_sys->p_text,
879                                            p_region, p_region );
880
881 #if defined(FBOSD_BLENDING)
882             fmt_out = p_region->fmt;
883             fmt_out.i_bits_per_pixel = 32;
884             vlc_memcpy( p_fmt, &fmt_out, sizeof(video_format_t) );
885
886             p_dest = AllocatePicture( VLC_OBJECT(p_intf), &fmt_out );
887             if( !p_dest )
888             {
889                 if( p_region->picture.pf_release )
890                     p_region->picture.pf_release( &p_region->picture );
891                 free( p_region->psz_text );
892                 free( p_region );
893                 return NULL;
894             }
895             vout_CopyPicture( VLC_OBJECT(p_intf), p_dest, &p_region->picture );
896 #else
897             fmt_out.i_chroma = p_fmt->i_chroma;
898             p_dest = ConvertImage( p_intf, &p_region->picture,
899                                    &p_region->fmt, &fmt_out );
900 #endif
901             if( p_region->picture.pf_release )
902                 p_region->picture.pf_release( &p_region->picture );
903             free( p_region->psz_text );
904             free( p_region );
905             return p_dest;
906         }
907         free( p_region->psz_text );
908         free( p_region );
909     }
910     return p_dest;
911 }
912
913 /*****************************************************************************
914  * LoadImage: Load an image from file into a picture buffer.
915  *****************************************************************************/
916 static picture_t *LoadImage( intf_thread_t *p_intf, video_format_t *p_fmt,
917                              char *psz_file )
918 {
919     picture_t  *p_pic = NULL;
920
921     if( psz_file && p_intf->p_sys->p_image )
922     {
923         video_format_t fmt_in, fmt_out;
924
925         memset( &fmt_in, 0, sizeof(fmt_in) );
926         memset( &fmt_out, 0, sizeof(fmt_out) );
927
928         fmt_out.i_chroma = p_fmt->i_chroma;
929         p_pic = image_ReadUrl( p_intf->p_sys->p_image, psz_file,
930                                &fmt_in, &fmt_out );
931
932         msg_Dbg( p_intf, "image size %dx%d chroma %4.4s",
933                  fmt_out.i_width, fmt_out.i_height,
934                  (char *)&p_fmt->i_chroma );
935     }
936     return p_pic;
937 }
938
939 #if ! defined(FBOSD_BLENDING)
940 /*****************************************************************************
941  * Convertmage: Convert image to another fourcc
942  *****************************************************************************/
943 static picture_t *ConvertImage( intf_thread_t *p_intf, picture_t *p_pic,
944                          video_format_t *p_fmt_in, video_format_t *p_fmt_out )
945 {
946     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
947     picture_t  *p_old = NULL;
948
949     if( p_sys->p_image )
950     {
951         p_old = image_Convert( p_sys->p_image, p_pic, p_fmt_in, p_fmt_out );
952
953         msg_Dbg( p_intf, "converted image size %dx%d chroma %4.4s",
954                  p_fmt_out->i_width, p_fmt_out->i_height,
955                  (char *)&p_fmt_out->i_chroma );
956     }
957     return p_old;
958 }
959 #endif
960
961 /*****************************************************************************
962  * Init: initialize framebuffer video thread output method
963  *****************************************************************************/
964 static int Init( intf_thread_t *p_intf )
965 {
966     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
967
968     /* Initialize the output structure: RGB with square pixels, whatever
969      * the input format is, since it's the only format we know */
970     switch( p_sys->var_info.bits_per_pixel )
971     {
972     case 8: /* FIXME: set the palette */
973         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','G','B','2'); break;
974     case 15:
975         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','5'); break;
976     case 16:
977         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','6'); break;
978     case 24:
979         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','2','4'); break;
980     case 32:
981         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','3','2'); break;
982     default:
983         msg_Err( p_intf, "unknown screen depth %i",
984                  p_sys->var_info.bits_per_pixel );
985         return VLC_EGENERIC;
986     }
987
988     p_sys->fmt_out.i_bits_per_pixel = p_sys->var_info.bits_per_pixel;
989     p_sys->fmt_out.i_width  = p_sys->i_width;
990     p_sys->fmt_out.i_height = p_sys->i_height;
991
992     /* Assume we have square pixels */
993     if( p_sys->i_aspect < 0 )
994     {
995         p_sys->fmt_out.i_aspect = ( p_sys->i_width
996                                   * VOUT_ASPECT_FACTOR ) / p_sys->i_height;
997     }
998     else p_sys->fmt_out.i_aspect = p_sys->i_aspect;
999
1000     p_sys->fmt_out.i_sar_num = p_sys->fmt_out.i_sar_den = 1;
1001
1002     /* Allocate overlay buffer */
1003     p_sys->p_overlay = AllocatePicture( &p_sys->fmt_out );
1004     if( !p_sys->p_overlay ) return VLC_EGENERIC;
1005
1006     SetOverlayTransparency( p_intf, true );
1007
1008     /* We know the chroma, allocate a buffer which will be used
1009      * to write to the overlay framebuffer */
1010     p_sys->p_overlay->p->i_pixel_pitch = p_sys->i_bytes_per_pixel;
1011     p_sys->p_overlay->p->i_lines = p_sys->var_info.yres;
1012     p_sys->p_overlay->p->i_visible_lines = p_sys->var_info.yres;
1013
1014     if( p_sys->var_info.xres_virtual )
1015     {
1016         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres_virtual
1017                              * p_sys->i_bytes_per_pixel;
1018     }
1019     else
1020     {
1021         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres
1022                              * p_sys->i_bytes_per_pixel;
1023     }
1024
1025     p_sys->p_overlay->p->i_visible_pitch = p_sys->var_info.xres
1026                                  * p_sys->i_bytes_per_pixel;
1027
1028     p_sys->p_overlay->i_planes = 1;
1029
1030     return VLC_SUCCESS;
1031 }
1032
1033 /*****************************************************************************
1034  * End: terminate framebuffer interface
1035  *****************************************************************************/
1036 static void End( intf_thread_t *p_intf )
1037 {
1038     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
1039
1040     /* CleanUp */
1041     SetOverlayTransparency( p_intf, false );
1042     if( p_sys->p_overlay )
1043     {
1044         int ret;
1045         ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1046                      p_sys->i_page_size );
1047         if( ret < 0 )
1048             msg_Err( p_intf, "unable to clear overlay" );
1049     }
1050
1051     DeAllocatePicture( p_intf->p_sys->p_overlay,
1052                        &p_intf->p_sys->fmt_out );
1053     p_intf->p_sys->p_overlay = NULL;
1054 }
1055
1056 /*****************************************************************************
1057  * OpenDisplay: initialize framebuffer
1058  *****************************************************************************/
1059 static int OpenDisplay( intf_thread_t *p_intf )
1060 {
1061     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
1062     char *psz_device;                             /* framebuffer device path */
1063     struct fb_fix_screeninfo    fix_info;     /* framebuffer fix information */
1064
1065     /* Open framebuffer device */
1066     if( !(psz_device = config_GetPsz( p_intf, "fbosd-dev" )) )
1067     {
1068         msg_Err( p_intf, "don't know which fb osd/overlay device to open" );
1069         return VLC_EGENERIC;
1070     }
1071
1072     p_sys->i_fd = open( psz_device, O_RDWR );
1073     if( p_sys->i_fd == -1 )
1074     {
1075         msg_Err( p_intf, "cannot open %s (%s)", psz_device, strerror(errno) );
1076         free( psz_device );
1077         return VLC_EGENERIC;
1078     }
1079     free( psz_device );
1080
1081     /* Get framebuffer device information */
1082     if( ioctl( p_sys->i_fd, FBIOGET_VSCREENINFO, &p_sys->var_info ) )
1083     {
1084         msg_Err( p_intf, "cannot get fb info (%s)", strerror(errno) );
1085         close( p_sys->i_fd );
1086         return VLC_EGENERIC;
1087     }
1088
1089     /* Get some info on the framebuffer itself */
1090     if( ioctl( p_sys->i_fd, FBIOGET_FSCREENINFO, &fix_info ) == 0 )
1091     {
1092         p_sys->i_width = p_sys->fmt_out.i_width = p_sys->var_info.xres;
1093         p_sys->i_height = p_sys->fmt_out.i_height = p_sys->var_info.yres;
1094     }
1095
1096     /* FIXME: if the image is full-size, it gets cropped on the left
1097      * because of the xres / xres_virtual slight difference */
1098     msg_Dbg( p_intf, "%ix%i (virtual %ix%i)",
1099              p_sys->var_info.xres, p_sys->var_info.yres,
1100              p_sys->var_info.xres_virtual,
1101              p_sys->var_info.yres_virtual );
1102
1103     p_sys->fmt_out.i_width = p_sys->i_width;
1104     p_sys->fmt_out.i_height = p_sys->i_height;
1105
1106     p_sys->p_palette = NULL;
1107     p_sys->b_pan = ( fix_info.ypanstep || fix_info.ywrapstep );
1108
1109     switch( p_sys->var_info.bits_per_pixel )
1110     {
1111     case 8:
1112         p_sys->p_palette = malloc( 8 * 256 * sizeof( uint16_t ) );
1113         if( !p_sys->p_palette )
1114         {
1115             close( p_sys->i_fd );
1116             return VLC_ENOMEM;
1117         }
1118         p_sys->fb_cmap.start = 0;
1119         p_sys->fb_cmap.len = 256;
1120         p_sys->fb_cmap.red = p_sys->p_palette;
1121         p_sys->fb_cmap.green  = p_sys->p_palette + 256 * sizeof( uint16_t );
1122         p_sys->fb_cmap.blue   = p_sys->p_palette + 2 * 256 * sizeof( uint16_t );
1123         p_sys->fb_cmap.transp = p_sys->p_palette + 3 * 256 * sizeof( uint16_t );
1124
1125         /* Save the colormap */
1126         ioctl( p_sys->i_fd, FBIOGETCMAP, &p_sys->fb_cmap );
1127
1128         p_sys->i_bytes_per_pixel = 1;
1129         break;
1130
1131     case 15:
1132     case 16:
1133         p_sys->i_bytes_per_pixel = 2;
1134         break;
1135
1136     case 24:
1137         p_sys->i_bytes_per_pixel = 3;
1138         break;
1139
1140     case 32:
1141         p_sys->i_bytes_per_pixel = 4;
1142         break;
1143
1144     default:
1145         msg_Err( p_intf, "screen depth %d is not supported",
1146                          p_sys->var_info.bits_per_pixel );
1147
1148         close( p_sys->i_fd );
1149         return VLC_EGENERIC;
1150     }
1151
1152     p_sys->i_page_size = p_sys->i_width * p_sys->i_height
1153                          * p_sys->i_bytes_per_pixel;
1154
1155     msg_Dbg( p_intf, "framebuffer type=%d, visual=%d, ypanstep=%d, "
1156              "ywrap=%d, accel=%d", fix_info.type, fix_info.visual,
1157              fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
1158     return VLC_SUCCESS;
1159 }
1160
1161 /*****************************************************************************
1162  * CloseDisplay: terminate FB interface thread
1163  *****************************************************************************/
1164 static void CloseDisplay( intf_thread_t *p_intf )
1165 {
1166     intf_sys_t *p_sys = (intf_sys_t *) p_intf;
1167
1168     /* Restore palette */
1169     if( p_sys->var_info.bits_per_pixel == 8 )
1170     {
1171         ioctl( p_sys->i_fd, FBIOPUTCMAP, &p_sys->fb_cmap );
1172         free( p_sys->p_palette );
1173         p_sys->p_palette = NULL;
1174     }
1175
1176     /* Close fb */
1177     close( p_sys->i_fd );
1178 }
1179
1180 static void Render( intf_thread_t *p_intf, struct fbosd_render_t *render )
1181 {
1182     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1183
1184     if( render->i_state != FBOSD_STATE_RENDER ) return;
1185     if( !render->psz_string ) return;
1186
1187     if( render->i_type == FBOSD_RENDER_IMAGE )
1188     {
1189         picture_t *p_pic;
1190         p_pic = LoadImage( p_intf, &p_sys->fmt_out, render->psz_string );
1191         if( p_pic )
1192         {
1193             RenderPicture( p_intf, render->i_x, render->i_y,
1194                            p_pic, p_sys->p_overlay );
1195             picture_Release( p_pic );
1196         }
1197     }
1198     else if( render->i_type == FBOSD_RENDER_TEXT )
1199     {
1200         picture_t *p_text;
1201 #if defined(FBOSD_BLENDING)
1202         video_format_t fmt_in;
1203         memset( &fmt_in, 0, sizeof(video_format_t) );
1204         p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1205                              &fmt_in );
1206         if( p_text )
1207         {
1208             BlendPicture( p_intf, &fmt_in, &p_sys->fmt_out,
1209                           p_text, p_sys->p_overlay );
1210             msg_Dbg( p_intf, "releasing picture" );
1211             DeAllocatePicture( p_text, &fmt_in );
1212         }
1213 #else
1214         p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1215                              &p_sys->fmt_out );
1216         if( p_text )
1217         {
1218             RenderPicture( p_intf, render->i_x, render->i_y,
1219                            p_text, p_sys->p_overlay );
1220             picture_Release( p_text );
1221         }
1222 #endif
1223     }
1224 }
1225
1226 static void RenderClear( intf_thread_t *p_intf, struct fbosd_render_t *render )
1227 {
1228     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1229
1230     vlc_memcpy( &render->text_style, &default_text_style,
1231                 sizeof( text_style_t ) );
1232     free( render->psz_string );
1233     render->psz_string = NULL;
1234
1235     render->i_x = p_sys->i_x;
1236     render->i_y = p_sys->i_y;
1237     render->i_pos = p_sys->i_pos;
1238     render->i_alpha = p_sys->i_alpha;
1239     render->b_absolute = p_sys->b_absolute;
1240     render->i_state = FBOSD_STATE_FREE;
1241 }
1242
1243 static bool isRendererReady( intf_thread_t *p_intf )
1244 {
1245     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1246     int i;
1247
1248     /* Check if there are more items to render */
1249     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1250     {
1251         if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1252             return false;
1253     }
1254     return true;
1255 }
1256
1257 /*****************************************************************************
1258  * Run: thread
1259  *****************************************************************************
1260  * This part of the interface is in a separate thread so that we can call
1261  * exec() from within it without annoying the rest of the program.
1262  *****************************************************************************/
1263 static void Run( intf_thread_t *p_intf )
1264 {
1265     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1266     int canc = vlc_savecancel();
1267
1268     while( vlc_object_alive( p_intf ) )
1269     {
1270         int i;
1271
1272         /* Is there somthing to render? */
1273         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1274         {
1275             if( p_sys->render[i].i_state == FBOSD_STATE_RENDER )
1276             {
1277                 Render( p_intf, &p_sys->render[i] );
1278                 RenderClear( p_intf, &p_sys->render[i] );
1279             }
1280         }
1281
1282         if( p_sys->b_clear )
1283         {
1284             SetOverlayTransparency( p_intf, true );
1285
1286             var_SetString( p_intf, "fbosd-image", "" );
1287             var_SetString( p_intf, "fbosd-text", "" );
1288
1289             p_sys->b_clear = false;
1290             p_sys->b_need_update = true;
1291         }
1292
1293         if( p_sys->b_need_update && p_sys->p_overlay &&
1294             isRendererReady( p_intf ) )
1295         {
1296             int ret;
1297 #if defined(FBOSD_BLENDING)
1298             /* Reverse alpha channel to work around FPGA bug */
1299             InvertAlpha( p_intf, &p_sys->p_overlay, p_sys->fmt_out );
1300 #endif
1301             ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1302                          p_sys->i_page_size );
1303             if( ret < 0 )
1304                 msg_Err( p_intf, "unable to write to overlay" );
1305             lseek( p_sys->i_fd, 0, SEEK_SET );
1306
1307             /* clear the picture */
1308             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, p_sys->i_page_size );
1309             p_sys->b_need_update = false;
1310         }
1311
1312         msleep( INTF_IDLE_SLEEP );
1313     }
1314
1315     End( p_intf );
1316     vlc_restorecancel( canc );
1317 }
1318
1319 static int OverlayCallback( vlc_object_t *p_this, char const *psz_cmd,
1320                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1321 {
1322     intf_thread_t *p_intf = (intf_thread_t *) p_this;
1323     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1324     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1325
1326     if( !strncmp( psz_cmd, "fbosd-display", 13 ) )
1327     {
1328         p_sys->b_need_update = true;
1329     }
1330     else if( !strncmp( psz_cmd, "fbosd-clear", 11 ) )
1331     {
1332         int i;
1333         /* Clear the entire render list */
1334         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1335         {
1336             RenderClear( p_intf, &p_sys->render[i] );
1337         }
1338         p_sys->b_clear = true;
1339     }
1340     else if( !strncmp( psz_cmd, "fbosd-render", 12 ) )
1341     {
1342         int i;
1343         /* Are we already busy with on slot ? */
1344         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1345         {
1346             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1347             {
1348                 p_sys->render[i].i_state = FBOSD_STATE_RENDER;
1349                 break;
1350             }
1351         }
1352     }
1353     else
1354     {
1355         int i;
1356         /* Are we already busy with on slot ? */
1357         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1358         {
1359             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1360                 break;
1361         }
1362         /* No, then find first FREE slot */
1363         if( p_sys->render[i].i_state != FBOSD_STATE_RESERVED )
1364         {
1365             for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1366             {
1367                 if( p_sys->render[i].i_state == FBOSD_STATE_FREE )
1368                     break;
1369             }
1370             if( p_sys->render[i].i_state != FBOSD_STATE_FREE )
1371             {
1372                 msg_Warn( p_this, "render space depleated" );
1373                 return VLC_SUCCESS;
1374             }
1375         }
1376         /* Found a free slot */
1377         p_sys->render[i].i_state = FBOSD_STATE_RESERVED;
1378         if( !strncmp( psz_cmd, "fbosd-image", 11 ) )
1379         {
1380             free( p_sys->render[i].psz_string );
1381             p_sys->render[i].psz_string = strdup( newval.psz_string );
1382             p_sys->render[i].i_type = FBOSD_RENDER_IMAGE;
1383         }
1384         else if( !strncmp( psz_cmd, "fbosd-text", 10 ) )
1385         {
1386             free( p_sys->render[i].psz_string );
1387             p_sys->render[i].psz_string = strdup( newval.psz_string );
1388             p_sys->render[i].i_type = FBOSD_RENDER_TEXT;
1389         }
1390         else if( !strncmp( psz_cmd, "fbosd-x", 7 ) )
1391         {
1392             p_sys->render[i].b_absolute = false;
1393             p_sys->render[i].i_x = (newval.i_int < p_sys->i_width) ?
1394                                     newval.i_int : p_sys->i_width;
1395         }
1396         else if( !strncmp( psz_cmd, "fbosd-y", 7 ) )
1397         {
1398             p_sys->render[i].b_absolute = false;
1399             p_sys->render[i].i_y = (newval.i_int < p_sys->i_height) ?
1400                                     newval.i_int : p_sys->i_height;
1401         }
1402         else if( !strncmp( psz_cmd, "fbosd-position", 14 ) )
1403         {
1404             p_sys->render[i].b_absolute = true;
1405             p_sys->render[i].i_pos = newval.i_int;
1406         }
1407         else if( !strncmp( psz_cmd, "fbosd-font-size", 15 ) )
1408         {
1409             p_sys->render[i].text_style.i_font_size = newval.i_int;
1410         }
1411         else if( !strncmp( psz_cmd, "fbosd-font-color", 16 ) )
1412         {
1413             p_sys->render[i].text_style.i_font_color = newval.i_int;
1414         }
1415         else if( !strncmp( psz_cmd, "fbosd-font-opacity", 18 ) )
1416         {
1417             p_sys->render[i].text_style.i_font_alpha = 255 - newval.i_int;
1418         }
1419         else if( !strncmp( psz_cmd, "fbosd-alpha", 11 ) )
1420         {
1421             p_sys->render[i].i_alpha = newval.i_int;
1422         }
1423     }
1424     return VLC_SUCCESS;
1425 }