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