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