]> git.sesse.net Git - vlc/blob - modules/gui/fbosd.c
Removed vout_CopyPicture in favor of picture_Copy.
[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/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             /* FIXME not needed to copy the picture anymore no ? */
887             p_dest = AllocatePicture( VLC_OBJECT(p_intf), &fmt_out );
888             if( !p_dest )
889             {
890                 picture_Release( p_region->p_picture );
891                 free( p_region->psz_text );
892                 free( p_region );
893                 return NULL;
894             }
895             picture_Copy( p_dest, p_region->p_picture );
896 #else
897             fmt_out.i_chroma = p_fmt->i_chroma;
898             p_dest = ConvertImage( p_intf, &p_region->p_picture,
899                                    &p_region->fmt, &fmt_out );
900 #endif
901             picture_Release( p_region->p_picture );
902             free( p_region->psz_text );
903             free( p_region );
904             return p_dest;
905         }
906         free( p_region->psz_text );
907         free( p_region );
908     }
909     return p_dest;
910 }
911
912 /*****************************************************************************
913  * LoadImage: Load an image from file into a picture buffer.
914  *****************************************************************************/
915 static picture_t *LoadImage( intf_thread_t *p_intf, video_format_t *p_fmt,
916                              char *psz_file )
917 {
918     picture_t  *p_pic = NULL;
919
920     if( psz_file && p_intf->p_sys->p_image )
921     {
922         video_format_t fmt_in, fmt_out;
923
924         memset( &fmt_in, 0, sizeof(fmt_in) );
925         memset( &fmt_out, 0, sizeof(fmt_out) );
926
927         fmt_out.i_chroma = p_fmt->i_chroma;
928         p_pic = image_ReadUrl( p_intf->p_sys->p_image, psz_file,
929                                &fmt_in, &fmt_out );
930
931         msg_Dbg( p_intf, "image size %dx%d chroma %4.4s",
932                  fmt_out.i_width, fmt_out.i_height,
933                  (char *)&p_fmt->i_chroma );
934     }
935     return p_pic;
936 }
937
938 #if ! defined(FBOSD_BLENDING)
939 /*****************************************************************************
940  * Convertmage: Convert image to another fourcc
941  *****************************************************************************/
942 static picture_t *ConvertImage( intf_thread_t *p_intf, picture_t *p_pic,
943                          video_format_t *p_fmt_in, video_format_t *p_fmt_out )
944 {
945     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
946     picture_t  *p_old = NULL;
947
948     if( p_sys->p_image )
949     {
950         p_old = image_Convert( p_sys->p_image, p_pic, p_fmt_in, p_fmt_out );
951
952         msg_Dbg( p_intf, "converted image size %dx%d chroma %4.4s",
953                  p_fmt_out->i_width, p_fmt_out->i_height,
954                  (char *)&p_fmt_out->i_chroma );
955     }
956     return p_old;
957 }
958 #endif
959
960 /*****************************************************************************
961  * Init: initialize framebuffer video thread output method
962  *****************************************************************************/
963 static int Init( intf_thread_t *p_intf )
964 {
965     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
966
967     /* Initialize the output structure: RGB with square pixels, whatever
968      * the input format is, since it's the only format we know */
969     switch( p_sys->var_info.bits_per_pixel )
970     {
971     case 8: /* FIXME: set the palette */
972         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','G','B','2'); break;
973     case 15:
974         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','5'); break;
975     case 16:
976         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','6'); break;
977     case 24:
978         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','2','4'); break;
979     case 32:
980         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','3','2'); break;
981     default:
982         msg_Err( p_intf, "unknown screen depth %i",
983                  p_sys->var_info.bits_per_pixel );
984         return VLC_EGENERIC;
985     }
986
987     p_sys->fmt_out.i_bits_per_pixel = p_sys->var_info.bits_per_pixel;
988     p_sys->fmt_out.i_width  = p_sys->i_width;
989     p_sys->fmt_out.i_height = p_sys->i_height;
990
991     /* Assume we have square pixels */
992     if( p_sys->i_aspect < 0 )
993     {
994         p_sys->fmt_out.i_aspect = ( p_sys->i_width
995                                   * VOUT_ASPECT_FACTOR ) / p_sys->i_height;
996     }
997     else p_sys->fmt_out.i_aspect = p_sys->i_aspect;
998
999     p_sys->fmt_out.i_sar_num = p_sys->fmt_out.i_sar_den = 1;
1000
1001     /* Allocate overlay buffer */
1002     p_sys->p_overlay = AllocatePicture( &p_sys->fmt_out );
1003     if( !p_sys->p_overlay ) return VLC_EGENERIC;
1004
1005     SetOverlayTransparency( p_intf, true );
1006
1007     /* We know the chroma, allocate a buffer which will be used
1008      * to write to the overlay framebuffer */
1009     p_sys->p_overlay->p->i_pixel_pitch = p_sys->i_bytes_per_pixel;
1010     p_sys->p_overlay->p->i_lines = p_sys->var_info.yres;
1011     p_sys->p_overlay->p->i_visible_lines = p_sys->var_info.yres;
1012
1013     if( p_sys->var_info.xres_virtual )
1014     {
1015         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres_virtual
1016                              * p_sys->i_bytes_per_pixel;
1017     }
1018     else
1019     {
1020         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres
1021                              * p_sys->i_bytes_per_pixel;
1022     }
1023
1024     p_sys->p_overlay->p->i_visible_pitch = p_sys->var_info.xres
1025                                  * p_sys->i_bytes_per_pixel;
1026
1027     p_sys->p_overlay->i_planes = 1;
1028
1029     return VLC_SUCCESS;
1030 }
1031
1032 /*****************************************************************************
1033  * End: terminate framebuffer interface
1034  *****************************************************************************/
1035 static void End( intf_thread_t *p_intf )
1036 {
1037     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
1038
1039     /* CleanUp */
1040     SetOverlayTransparency( p_intf, false );
1041     if( p_sys->p_overlay )
1042     {
1043         int ret;
1044         ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1045                      p_sys->i_page_size );
1046         if( ret < 0 )
1047             msg_Err( p_intf, "unable to clear overlay" );
1048     }
1049
1050     DeAllocatePicture( p_intf->p_sys->p_overlay,
1051                        &p_intf->p_sys->fmt_out );
1052     p_intf->p_sys->p_overlay = NULL;
1053 }
1054
1055 /*****************************************************************************
1056  * OpenDisplay: initialize framebuffer
1057  *****************************************************************************/
1058 static int OpenDisplay( intf_thread_t *p_intf )
1059 {
1060     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
1061     char *psz_device;                             /* framebuffer device path */
1062     struct fb_fix_screeninfo    fix_info;     /* framebuffer fix information */
1063
1064     /* Open framebuffer device */
1065     if( !(psz_device = config_GetPsz( p_intf, "fbosd-dev" )) )
1066     {
1067         msg_Err( p_intf, "don't know which fb osd/overlay device to open" );
1068         return VLC_EGENERIC;
1069     }
1070
1071     p_sys->i_fd = open( psz_device, O_RDWR );
1072     if( p_sys->i_fd == -1 )
1073     {
1074         msg_Err( p_intf, "cannot open %s (%s)", psz_device, strerror(errno) );
1075         free( psz_device );
1076         return VLC_EGENERIC;
1077     }
1078     free( psz_device );
1079
1080     /* Get framebuffer device information */
1081     if( ioctl( p_sys->i_fd, FBIOGET_VSCREENINFO, &p_sys->var_info ) )
1082     {
1083         msg_Err( p_intf, "cannot get fb info (%s)", strerror(errno) );
1084         close( p_sys->i_fd );
1085         return VLC_EGENERIC;
1086     }
1087
1088     /* Get some info on the framebuffer itself */
1089     if( ioctl( p_sys->i_fd, FBIOGET_FSCREENINFO, &fix_info ) == 0 )
1090     {
1091         p_sys->i_width = p_sys->fmt_out.i_width = p_sys->var_info.xres;
1092         p_sys->i_height = p_sys->fmt_out.i_height = p_sys->var_info.yres;
1093     }
1094
1095     /* FIXME: if the image is full-size, it gets cropped on the left
1096      * because of the xres / xres_virtual slight difference */
1097     msg_Dbg( p_intf, "%ix%i (virtual %ix%i)",
1098              p_sys->var_info.xres, p_sys->var_info.yres,
1099              p_sys->var_info.xres_virtual,
1100              p_sys->var_info.yres_virtual );
1101
1102     p_sys->fmt_out.i_width = p_sys->i_width;
1103     p_sys->fmt_out.i_height = p_sys->i_height;
1104
1105     p_sys->p_palette = NULL;
1106     p_sys->b_pan = ( fix_info.ypanstep || fix_info.ywrapstep );
1107
1108     switch( p_sys->var_info.bits_per_pixel )
1109     {
1110     case 8:
1111         p_sys->p_palette = malloc( 8 * 256 * sizeof( uint16_t ) );
1112         if( !p_sys->p_palette )
1113         {
1114             close( p_sys->i_fd );
1115             return VLC_ENOMEM;
1116         }
1117         p_sys->fb_cmap.start = 0;
1118         p_sys->fb_cmap.len = 256;
1119         p_sys->fb_cmap.red = p_sys->p_palette;
1120         p_sys->fb_cmap.green  = p_sys->p_palette + 256 * sizeof( uint16_t );
1121         p_sys->fb_cmap.blue   = p_sys->p_palette + 2 * 256 * sizeof( uint16_t );
1122         p_sys->fb_cmap.transp = p_sys->p_palette + 3 * 256 * sizeof( uint16_t );
1123
1124         /* Save the colormap */
1125         ioctl( p_sys->i_fd, FBIOGETCMAP, &p_sys->fb_cmap );
1126
1127         p_sys->i_bytes_per_pixel = 1;
1128         break;
1129
1130     case 15:
1131     case 16:
1132         p_sys->i_bytes_per_pixel = 2;
1133         break;
1134
1135     case 24:
1136         p_sys->i_bytes_per_pixel = 3;
1137         break;
1138
1139     case 32:
1140         p_sys->i_bytes_per_pixel = 4;
1141         break;
1142
1143     default:
1144         msg_Err( p_intf, "screen depth %d is not supported",
1145                          p_sys->var_info.bits_per_pixel );
1146
1147         close( p_sys->i_fd );
1148         return VLC_EGENERIC;
1149     }
1150
1151     p_sys->i_page_size = p_sys->i_width * p_sys->i_height
1152                          * p_sys->i_bytes_per_pixel;
1153
1154     msg_Dbg( p_intf, "framebuffer type=%d, visual=%d, ypanstep=%d, "
1155              "ywrap=%d, accel=%d", fix_info.type, fix_info.visual,
1156              fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
1157     return VLC_SUCCESS;
1158 }
1159
1160 /*****************************************************************************
1161  * CloseDisplay: terminate FB interface thread
1162  *****************************************************************************/
1163 static void CloseDisplay( intf_thread_t *p_intf )
1164 {
1165     intf_sys_t *p_sys = (intf_sys_t *) p_intf;
1166
1167     /* Restore palette */
1168     if( p_sys->var_info.bits_per_pixel == 8 )
1169     {
1170         ioctl( p_sys->i_fd, FBIOPUTCMAP, &p_sys->fb_cmap );
1171         free( p_sys->p_palette );
1172         p_sys->p_palette = NULL;
1173     }
1174
1175     /* Close fb */
1176     close( p_sys->i_fd );
1177 }
1178
1179 static void Render( intf_thread_t *p_intf, struct fbosd_render_t *render )
1180 {
1181     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1182
1183     if( render->i_state != FBOSD_STATE_RENDER ) return;
1184     if( !render->psz_string ) return;
1185
1186     if( render->i_type == FBOSD_RENDER_IMAGE )
1187     {
1188         picture_t *p_pic;
1189         p_pic = LoadImage( p_intf, &p_sys->fmt_out, render->psz_string );
1190         if( p_pic )
1191         {
1192             RenderPicture( p_intf, render->i_x, render->i_y,
1193                            p_pic, p_sys->p_overlay );
1194             picture_Release( p_pic );
1195         }
1196     }
1197     else if( render->i_type == FBOSD_RENDER_TEXT )
1198     {
1199         picture_t *p_text;
1200 #if defined(FBOSD_BLENDING)
1201         video_format_t fmt_in;
1202         memset( &fmt_in, 0, sizeof(video_format_t) );
1203         p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1204                              &fmt_in );
1205         if( p_text )
1206         {
1207             BlendPicture( p_intf, &fmt_in, &p_sys->fmt_out,
1208                           p_text, p_sys->p_overlay );
1209             msg_Dbg( p_intf, "releasing picture" );
1210             DeAllocatePicture( p_text, &fmt_in );
1211         }
1212 #else
1213         p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1214                              &p_sys->fmt_out );
1215         if( p_text )
1216         {
1217             RenderPicture( p_intf, render->i_x, render->i_y,
1218                            p_text, p_sys->p_overlay );
1219             picture_Release( p_text );
1220         }
1221 #endif
1222     }
1223 }
1224
1225 static void RenderClear( intf_thread_t *p_intf, struct fbosd_render_t *render )
1226 {
1227     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1228
1229     vlc_memcpy( &render->text_style, &default_text_style,
1230                 sizeof( text_style_t ) );
1231     free( render->psz_string );
1232     render->psz_string = NULL;
1233
1234     render->i_x = p_sys->i_x;
1235     render->i_y = p_sys->i_y;
1236     render->i_pos = p_sys->i_pos;
1237     render->i_alpha = p_sys->i_alpha;
1238     render->b_absolute = p_sys->b_absolute;
1239     render->i_state = FBOSD_STATE_FREE;
1240 }
1241
1242 static bool isRendererReady( intf_thread_t *p_intf )
1243 {
1244     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1245     int i;
1246
1247     /* Check if there are more items to render */
1248     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1249     {
1250         if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1251             return false;
1252     }
1253     return true;
1254 }
1255
1256 /*****************************************************************************
1257  * Run: thread
1258  *****************************************************************************
1259  * This part of the interface is in a separate thread so that we can call
1260  * exec() from within it without annoying the rest of the program.
1261  *****************************************************************************/
1262 static void Run( intf_thread_t *p_intf )
1263 {
1264     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1265     int canc = vlc_savecancel();
1266
1267     while( vlc_object_alive( p_intf ) )
1268     {
1269         int i;
1270
1271         /* Is there somthing to render? */
1272         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1273         {
1274             if( p_sys->render[i].i_state == FBOSD_STATE_RENDER )
1275             {
1276                 Render( p_intf, &p_sys->render[i] );
1277                 RenderClear( p_intf, &p_sys->render[i] );
1278             }
1279         }
1280
1281         if( p_sys->b_clear )
1282         {
1283             SetOverlayTransparency( p_intf, true );
1284
1285             var_SetString( p_intf, "fbosd-image", "" );
1286             var_SetString( p_intf, "fbosd-text", "" );
1287
1288             p_sys->b_clear = false;
1289             p_sys->b_need_update = true;
1290         }
1291
1292         if( p_sys->b_need_update && p_sys->p_overlay &&
1293             isRendererReady( p_intf ) )
1294         {
1295             int ret;
1296 #if defined(FBOSD_BLENDING)
1297             /* Reverse alpha channel to work around FPGA bug */
1298             InvertAlpha( p_intf, &p_sys->p_overlay, p_sys->fmt_out );
1299 #endif
1300             ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1301                          p_sys->i_page_size );
1302             if( ret < 0 )
1303                 msg_Err( p_intf, "unable to write to overlay" );
1304             lseek( p_sys->i_fd, 0, SEEK_SET );
1305
1306             /* clear the picture */
1307             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, p_sys->i_page_size );
1308             p_sys->b_need_update = false;
1309         }
1310
1311         msleep( INTF_IDLE_SLEEP );
1312     }
1313
1314     End( p_intf );
1315     vlc_restorecancel( canc );
1316 }
1317
1318 static int OverlayCallback( vlc_object_t *p_this, char const *psz_cmd,
1319                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1320 {
1321     intf_thread_t *p_intf = (intf_thread_t *) p_this;
1322     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1323     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1324
1325     if( !strncmp( psz_cmd, "fbosd-display", 13 ) )
1326     {
1327         p_sys->b_need_update = true;
1328     }
1329     else if( !strncmp( psz_cmd, "fbosd-clear", 11 ) )
1330     {
1331         int i;
1332         /* Clear the entire render list */
1333         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1334         {
1335             RenderClear( p_intf, &p_sys->render[i] );
1336         }
1337         p_sys->b_clear = true;
1338     }
1339     else if( !strncmp( psz_cmd, "fbosd-render", 12 ) )
1340     {
1341         int i;
1342         /* Are we already busy with on slot ? */
1343         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1344         {
1345             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1346             {
1347                 p_sys->render[i].i_state = FBOSD_STATE_RENDER;
1348                 break;
1349             }
1350         }
1351     }
1352     else
1353     {
1354         int i;
1355         /* Are we already busy with on slot ? */
1356         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1357         {
1358             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1359                 break;
1360         }
1361         /* No, then find first FREE slot */
1362         if( p_sys->render[i].i_state != FBOSD_STATE_RESERVED )
1363         {
1364             for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1365             {
1366                 if( p_sys->render[i].i_state == FBOSD_STATE_FREE )
1367                     break;
1368             }
1369             if( p_sys->render[i].i_state != FBOSD_STATE_FREE )
1370             {
1371                 msg_Warn( p_this, "render space depleated" );
1372                 return VLC_SUCCESS;
1373             }
1374         }
1375         /* Found a free slot */
1376         p_sys->render[i].i_state = FBOSD_STATE_RESERVED;
1377         if( !strncmp( psz_cmd, "fbosd-image", 11 ) )
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_IMAGE;
1382         }
1383         else if( !strncmp( psz_cmd, "fbosd-text", 10 ) )
1384         {
1385             free( p_sys->render[i].psz_string );
1386             p_sys->render[i].psz_string = strdup( newval.psz_string );
1387             p_sys->render[i].i_type = FBOSD_RENDER_TEXT;
1388         }
1389         else if( !strncmp( psz_cmd, "fbosd-x", 7 ) )
1390         {
1391             p_sys->render[i].b_absolute = false;
1392             p_sys->render[i].i_x = (newval.i_int < p_sys->i_width) ?
1393                                     newval.i_int : p_sys->i_width;
1394         }
1395         else if( !strncmp( psz_cmd, "fbosd-y", 7 ) )
1396         {
1397             p_sys->render[i].b_absolute = false;
1398             p_sys->render[i].i_y = (newval.i_int < p_sys->i_height) ?
1399                                     newval.i_int : p_sys->i_height;
1400         }
1401         else if( !strncmp( psz_cmd, "fbosd-position", 14 ) )
1402         {
1403             p_sys->render[i].b_absolute = true;
1404             p_sys->render[i].i_pos = newval.i_int;
1405         }
1406         else if( !strncmp( psz_cmd, "fbosd-font-size", 15 ) )
1407         {
1408             p_sys->render[i].text_style.i_font_size = newval.i_int;
1409         }
1410         else if( !strncmp( psz_cmd, "fbosd-font-color", 16 ) )
1411         {
1412             p_sys->render[i].text_style.i_font_color = newval.i_int;
1413         }
1414         else if( !strncmp( psz_cmd, "fbosd-font-opacity", 18 ) )
1415         {
1416             p_sys->render[i].text_style.i_font_alpha = 255 - newval.i_int;
1417         }
1418         else if( !strncmp( psz_cmd, "fbosd-alpha", 11 ) )
1419         {
1420             p_sys->render[i].i_alpha = newval.i_int;
1421         }
1422     }
1423     return VLC_SUCCESS;
1424 }