fbosd.c 49.8 KB
Newer Older
1 2 3
/*****************************************************************************
 * fbosd.c : framebuffer osd plugin for vlc
 *****************************************************************************
4
 * Copyright (C) 2007-2008, the VideoLAN team
Pierre's avatar
Pierre committed
5
 * $Id$
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *
 * Authors: Jean-Paul Saman
 * Copied from modules/video_output/fb.c by Samuel Hocevar <sam@zoy.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
28 29 30 31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32
#include <vlc_common.h>
33
#include <vlc_plugin.h>
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53

#include <errno.h>
#include <stdlib.h>                                                /* free() */
#include <string.h>                                            /* strerror() */
#include <fcntl.h>                                                 /* open() */
#include <unistd.h>                                               /* close() */

#include <sys/ioctl.h>
#include <sys/mman.h>                                              /* mmap() */

#include <linux/fb.h>

#include <vlc_image.h>
#include <vlc_interface.h>
#include <vlc_input.h>
#include <vlc_vout.h>
#include <vlc_filter.h>
#include <vlc_osd.h>
#include <vlc_strings.h>

Jean-Paul Saman's avatar
Jean-Paul Saman committed
54 55
#undef FBOSD_BLENDING
#undef FBOSD_DEBUG
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int  Create    ( vlc_object_t * );
static void Destroy   ( vlc_object_t * );
static void Run       ( intf_thread_t * );

static int  Init      ( intf_thread_t * );
static void End       ( intf_thread_t * );

static int  OpenDisplay    ( intf_thread_t * );
static void CloseDisplay   ( intf_thread_t * );

/* Load modules needed for rendering and blending */
71
#if defined(FBOSD_BLENDING)
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
static int  OpenBlending     ( intf_thread_t * );
static void CloseBlending    ( intf_thread_t * );
#endif
static int  OpenTextRenderer ( intf_thread_t * );
static void CloseTextRenderer( intf_thread_t * );

/* Manipulate the overlay buffer */
static int  OverlayCallback( vlc_object_t *, char const *,
                             vlc_value_t, vlc_value_t, void * );

static picture_t *AllocatePicture( vlc_object_t *,
                                         video_format_t * );
static void DeAllocatePicture( vlc_object_t *, picture_t *,
                                     video_format_t * );
static void SetOverlayTransparency( intf_thread_t *,
87
                                    bool );
88 89 90
static picture_t *LoadImage( intf_thread_t *, video_format_t *,
                             char * );

91
#if defined(FBOSD_BLENDING)
92 93 94 95 96 97 98 99 100
static int BlendPicture( intf_thread_t *, video_format_t *,
                         video_format_t *, picture_t *, picture_t * );
#else
static picture_t *ConvertImage( intf_thread_t *, picture_t *,
                                video_format_t *, video_format_t * );
#endif
static int RenderPicture( intf_thread_t *, int, int,
                          picture_t *, picture_t * );
static picture_t *RenderText( intf_thread_t *, const char *,
101
                              text_style_t *, video_format_t * );
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161

#define DEVICE_TEXT N_("Framebuffer device")
#define DEVICE_LONGTEXT N_( \
    "Framebuffer device to use for rendering (usually /dev/fb0).")

#define ASPECT_RATIO_TEXT N_("Video aspect ratio")
#define ASPECT_RATIO_LONGTEXT N_( \
    "Aspect ratio of the video image (4:3, 16:9). Default is square pixels." )

#define FBOSD_IMAGE_TEXT N_("Image file")
#define FBOSD_IMAGE_LONGTEXT N_( \
    "Filename of image file to use on the overlay framebuffer." )

#define ALPHA_TEXT N_("Transparency of the image")
#define ALPHA_LONGTEXT N_( "Transparency value of the new image " \
    "used in blending. By default it set to fully opaque (255). " \
    "(from 0 for full transparency to 255 for full opacity)" )

#define FBOSD_TEXT N_("Text")
#define FBOSD_LONGTEXT N_( "Text to display on the overlay framebuffer." )

#define POSX_TEXT N_("X coordinate")
#define POSX_LONGTEXT N_("X coordinate of the rendered image")

#define POSY_TEXT N_("Y coordinate")
#define POSY_LONGTEXT N_("Y coordinate of the rendered image")

#define POS_TEXT N_("Position")
#define POS_LONGTEXT N_( \
  "You can enforce the picture position on the overlay " \
  "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
  "also use combinations of these values, e.g. 6=top-right).")

#define OPACITY_TEXT N_("Opacity")
#define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
    "overlayed text. 0 = transparent, 255 = totally opaque. " )

#define SIZE_TEXT N_("Font size, pixels")
#define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
    "font size)." )

#define COLOR_TEXT N_("Color")
#define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
    "the video. This must be an hexadecimal (like HTML colors). The first two "\
    "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
    " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )

#define CLEAR_TEXT N_( "Clear overlay framebuffer" )
#define CLEAR_LONGTEXT N_( "The displayed overlay images is cleared by " \
    "making the overlay completely transparent. All previously rendered " \
    "images and text will be cleared from the cache." )

#define RENDER_TEXT N_( "Render text or image" )
#define RENDER_LONGTEXT N_( "Render the image or text in current overlay " \
    "buffer." )

#define DISPLAY_TEXT N_( "Display on overlay framebuffer" )
#define DISPLAY_LONGTEXT N_( "All rendered images and text will be " \
    "displayed on the overlay framebuffer." )

162 163
static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
static const char *const ppsz_pos_descriptions[] =
164 165 166
{ N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
  N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };

167 168
static const int pi_color_values[] = {
               0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
169 170 171
               0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
               0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
               0x00000080, 0x000000FF, 0x0000FFFF};
172 173
static const char *const ppsz_color_descriptions[] = {
               N_("Default"), N_("Black"),
174 175 176 177 178 179 180 181 182 183 184
               N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
               N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
               N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
               N_("Aqua") };

vlc_module_begin();
    set_shortname( "fbosd" );
    set_category( CAT_INTERFACE );
    set_subcategory( SUBCAT_INTERFACE_MAIN );

    add_file( "fbosd-dev", "/dev/fb1", NULL, DEVICE_TEXT, DEVICE_LONGTEXT,
185
              false );
186
    add_string( "fbosd-aspect-ratio", "", NULL, ASPECT_RATIO_TEXT,
187
                ASPECT_RATIO_LONGTEXT, true );
188 189

    add_string( "fbosd-image", NULL, NULL, FBOSD_IMAGE_TEXT,
190
                FBOSD_IMAGE_LONGTEXT, true );
191
    add_string( "fbosd-text", NULL, NULL, FBOSD_TEXT,
192
                FBOSD_LONGTEXT, true );
193 194

    add_integer_with_range( "fbosd-alpha", 255, 0, 255, NULL, ALPHA_TEXT,
195
                            ALPHA_LONGTEXT, true );
196 197 198

    set_section( N_("Position"), NULL );
    add_integer( "fbosd-x", 0, NULL, POSX_TEXT,
199
                 POSX_LONGTEXT, false );
200
    add_integer( "fbosd-y", 0, NULL, POSY_TEXT,
201 202
                 POSY_LONGTEXT, false );
    add_integer( "fbosd-position", 8, NULL, POS_TEXT, POS_LONGTEXT, true );
203
        change_integer_list( pi_pos_values, ppsz_pos_descriptions, NULL );
204 205 206

    set_section( N_("Font"), NULL );
    add_integer_with_range( "fbosd-font-opacity", 255, 0, 255, NULL,
207
        OPACITY_TEXT, OPACITY_LONGTEXT, false );
208
    add_integer( "fbosd-font-color", 0x00FFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
209
                 false );
210
        change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
211
    add_integer( "fbosd-font-size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT,
212
                 false );
213 214

    set_section( N_("Commands"), NULL );
215 216 217
    add_bool( "fbosd-clear", false, NULL, CLEAR_TEXT, CLEAR_LONGTEXT, true );
    add_bool( "fbosd-render", false, NULL, RENDER_TEXT, RENDER_LONGTEXT, true );
    add_bool( "fbosd-display", false, NULL, DISPLAY_TEXT, DISPLAY_LONGTEXT, true );
218

219
    set_description( N_("GNU/Linux osd/overlay framebuffer interface") );
220 221 222 223
    set_capability( "interface", 10 );
    set_callbacks( Create, Destroy );
vlc_module_end();

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
/*****************************************************************************
 * fbosd_render_t: render descriptor
 *****************************************************************************/
struct fbosd_render_t
{
#define FBOSD_RENDER_IMAGE 0
#define FBOSD_RENDER_TEXT  1
    int             i_type;

#define FBOSD_STATE_FREE     0
#define FBOSD_STATE_RESERVED 1
#define FBOSD_STATE_RENDER   2
    int             i_state;

    /* Font style */
    text_style_t    text_style;                              /* font control */
    char            *psz_string;

Rafaël Carré's avatar
Typo  
Rafaël Carré committed
242
    /* Position */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
243
    bool            b_absolute;
244 245 246 247 248 249 250
    int             i_x;
    int             i_y;
    int             i_pos;
    int             i_alpha;                      /* transparency for images */
};
#define FBOSD_RENDER_MAX 10

251 252 253 254 255 256 257 258
/*****************************************************************************
 * intf_sys_t: interface framebuffer method descriptor
 *****************************************************************************/
struct intf_sys_t
{
    /* Framebuffer information */
    int                         i_fd;                       /* device handle */
    struct fb_var_screeninfo    var_info;        /* current mode information */
259
    bool                  b_pan;     /* does device supports panning ? */
260 261 262 263 264 265 266 267 268 269 270 271 272 273
    struct fb_cmap              fb_cmap;                /* original colormap */
    uint16_t                    *p_palette;              /* original palette */

    /* Overlay framebuffer format */
    video_format_t  fmt_out;
    picture_t       *p_overlay;
    size_t          i_page_size;                                /* page size */
    int             i_width;
    int             i_height;
    int             i_aspect;
    int             i_bytes_per_pixel;

    /* Image and Picture rendering */
    image_handler_t *p_image;
274
#if defined(FBOSD_BLENDING)
275 276 277 278
    filter_t *p_blend;                              /* alpha blending module */
#endif
    filter_t *p_text;                                /* text renderer module */

279 280
    /* Render */
    struct fbosd_render_t render[FBOSD_RENDER_MAX];
281 282 283 284

    /* Font style */
    text_style_t    *p_style;                                /* font control */

Rafaël Carré's avatar
Typo  
Rafaël Carré committed
285
    /* Position */
286
    bool      b_absolute;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
287 288 289
    int       i_x;
    int       i_y;
    int       i_pos;
290

Jean-Paul Saman's avatar
Jean-Paul Saman committed
291
    int       i_alpha;                      /* transparency for images */
292 293

    /* commands control */
294 295 296
    bool      b_need_update;    /* update display with \overlay buffer */
    bool      b_clear;      /* clear overlay buffer make it tranparent */
    bool      b_render;   /* render an image or text in overlay buffer */
297 298 299 300 301 302 303 304 305 306
};

/*****************************************************************************
 * Create: allocates FB interface thread output method
 *****************************************************************************/
static int Create( vlc_object_t *p_this )
{
    intf_thread_t *p_intf = (intf_thread_t *)p_this;
    intf_sys_t    *p_sys;
    char          *psz_aspect;
307 308 309
    char          *psz_tmp;
    int i;

310 311 312 313 314 315 316 317 318 319 320 321
    /* Allocate instance and initialize some members */
    p_intf->p_sys = p_sys = malloc( sizeof( intf_sys_t ) );
    if( !p_intf->p_sys )
        return VLC_ENOMEM;
    memset( p_sys, 0, sizeof(intf_sys_t) );

    p_sys->p_style = malloc( sizeof( text_style_t ) );
    if( !p_sys->p_style )
    {
        free( p_intf->p_sys );
        return VLC_ENOMEM;
    }
322
    vlc_memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ) );
323 324 325 326 327 328 329 330 331 332 333 334 335

    p_intf->pf_run = Run;

    p_sys->p_image = image_HandlerCreate( p_this );
    if( !p_sys->p_image )
    {
        free( p_intf->p_sys->p_style );
        free( p_intf->p_sys );
        return VLC_ENOMEM;
    }

    p_sys->i_alpha = var_CreateGetIntegerCommand( p_intf, "fbosd-alpha" );
    var_AddCallback( p_intf, "fbosd-alpha", OverlayCallback, NULL );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
336

337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
    p_sys->i_aspect = -1;
    psz_aspect =
            var_CreateGetNonEmptyString( p_intf, "fbosd-aspect-ratio" );
    if( psz_aspect )
    {
        char *psz_parser = strchr( psz_aspect, ':' );

        if( psz_parser )
        {
            *psz_parser++ = '\0';
            p_sys->i_aspect = ( atoi( psz_aspect )
                              * VOUT_ASPECT_FACTOR ) / atoi( psz_parser );
            p_sys->fmt_out.i_aspect = p_sys->i_aspect;
        }
        msg_Dbg( p_intf, "using aspect ratio %d:%d",
                  atoi( psz_aspect ), atoi( psz_parser ) );

        free( psz_aspect );
        psz_aspect = NULL;
    }

    /* Use PAL by default */
    p_sys->i_width  = p_sys->fmt_out.i_width  = 704;
    p_sys->i_height = p_sys->fmt_out.i_height = 576;

362
    psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-image" );
363
    var_AddCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
364 365 366 367 368 369 370
    if( psz_tmp && *psz_tmp )
    {
        p_sys->render[0].i_type = FBOSD_RENDER_IMAGE;
        p_sys->render[0].i_state = FBOSD_STATE_RENDER;
        p_sys->render[0].psz_string = strdup( psz_tmp );
    }
    free( psz_tmp );
371

372
    psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-text" );
373
    var_AddCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
374 375 376 377 378 379 380
    if( psz_tmp && *psz_tmp )
    {
        p_sys->render[1].i_type = FBOSD_RENDER_TEXT;
        p_sys->render[1].i_state = FBOSD_STATE_RENDER;
        p_sys->render[1].psz_string = strdup( psz_tmp );
    }
    free( psz_tmp );
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400

    p_sys->i_pos = var_CreateGetIntegerCommand( p_intf, "fbosd-position" );
    p_sys->i_x = var_CreateGetIntegerCommand( p_intf, "fbosd-x" );
    p_sys->i_y = var_CreateGetIntegerCommand( p_intf, "fbosd-y" );

    var_AddCallback( p_intf, "fbosd-position", OverlayCallback, NULL );
    var_AddCallback( p_intf, "fbosd-x", OverlayCallback, NULL );
    var_AddCallback( p_intf, "fbosd-y", OverlayCallback, NULL );

    p_sys->p_style->i_font_size =
            var_CreateGetIntegerCommand( p_intf, "fbosd-font-size" );
    p_sys->p_style->i_font_color =
            var_CreateGetIntegerCommand( p_intf, "fbosd-font-color" );
    p_sys->p_style->i_font_alpha = 255 -
            var_CreateGetIntegerCommand( p_intf, "fbosd-font-opacity" );

    var_AddCallback( p_intf, "fbosd-font-color", OverlayCallback, NULL );
    var_AddCallback( p_intf, "fbosd-font-size", OverlayCallback, NULL );
    var_AddCallback( p_intf, "fbosd-font-opacity", OverlayCallback, NULL );

401 402
    for( i = 0; i < FBOSD_RENDER_MAX; i++ )
    {
403 404
        vlc_memcpy( &p_sys->render[i].text_style, &default_text_style,
                    sizeof( text_style_t ) );
405 406
    }

407 408 409 410 411 412 413 414 415
    p_sys->b_clear = var_CreateGetBoolCommand( p_intf, "fbosd-clear" );
    p_sys->b_render = var_CreateGetBoolCommand( p_intf, "fbosd-render" );
    p_sys->b_need_update = var_CreateGetBoolCommand( p_intf, "fbosd-display" );

    var_AddCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
    var_AddCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
    var_AddCallback( p_intf, "fbosd-display", OverlayCallback, NULL );

    /* Check if picture position was overridden */
416
    p_sys->b_absolute = true;
417 418
    if( ( p_sys->i_x >= 0 ) && ( p_sys->i_y >= 0 ) )
    {
419
        p_sys->b_absolute = false;
420 421 422 423 424 425
        p_sys->i_y = (p_sys->i_y < p_sys->i_height) ?
                        p_sys->i_y : p_sys->i_height;
        p_sys->i_x = (p_sys->i_x < p_sys->i_width) ?
                        p_sys->i_x : p_sys->i_width;
    }

426 427 428 429 430
    p_sys->render[0].i_x = p_sys->render[1].i_x = p_sys->i_x;
    p_sys->render[0].i_y = p_sys->render[1].i_y = p_sys->i_y;
    p_sys->render[0].i_pos = p_sys->render[1].i_pos = p_sys->i_pos;
    p_sys->render[0].i_alpha = p_sys->render[1].i_alpha = p_sys->i_alpha;

431 432 433 434 435 436 437 438 439
    /* Initialize framebuffer */
    if( OpenDisplay( p_intf ) )
    {
        Destroy( VLC_OBJECT(p_intf) );
        return VLC_EGENERIC;
    }

    Init( p_intf );

440
#if defined(FBOSD_BLENDING)
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
    /* Load the blending module */
    if( OpenBlending( p_intf ) )
    {
        msg_Err( p_intf, "Unable to load image blending module" );
        Destroy( VLC_OBJECT(p_intf) );
        return VLC_EGENERIC;
    }
#endif

    /* Load text renderer module */
    if( OpenTextRenderer( p_intf ) )
    {
        msg_Err( p_intf, "Unable to load text rendering module" );
        Destroy( VLC_OBJECT(p_intf) );
        return VLC_EGENERIC;
    }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
457

458 459
    p_sys->b_render = true;
    p_sys->b_need_update = true;
460 461 462 463 464 465 466 467 468 469 470 471 472

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Destroy: destroy FB interface thread output method
 *****************************************************************************
 * Terminate an output method created by Create
 *****************************************************************************/
static void Destroy( vlc_object_t *p_this )
{
    intf_thread_t *p_intf = (intf_thread_t *)p_this;
    intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
473
    int i;
474

475 476 477
    p_sys->b_need_update = false;
    p_sys->b_render = false;
    p_sys->b_clear = false;
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509

    var_DelCallback( p_intf, "fbosd-alpha", OverlayCallback, NULL );
    var_Destroy( p_intf, "fbosd-alpha" );

    var_DelCallback( p_intf, "fbosd-x", OverlayCallback, NULL );
    var_DelCallback( p_intf, "fbosd-y", OverlayCallback, NULL );
    var_DelCallback( p_intf, "fbosd-position", OverlayCallback, NULL );
    var_DelCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
    var_DelCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
    var_DelCallback( p_intf, "fbosd-font-size", OverlayCallback, NULL );
    var_DelCallback( p_intf, "fbosd-font-color", OverlayCallback, NULL );
    var_DelCallback( p_intf, "fbosd-font-opacity", OverlayCallback, NULL );
    var_DelCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
    var_DelCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
    var_DelCallback( p_intf, "fbosd-display", OverlayCallback, NULL );

    var_Destroy( p_intf, "fbosd-x" );
    var_Destroy( p_intf, "fbosd-y" );
    var_Destroy( p_intf, "fbosd-position" );
    var_Destroy( p_intf, "fbosd-image" );
    var_Destroy( p_intf, "fbosd-text" );
    var_Destroy( p_intf, "fbosd-font-size" );
    var_Destroy( p_intf, "fbosd-font-color" );
    var_Destroy( p_intf, "fbosd-font-opacity" );
    var_Destroy( p_intf, "fbosd-clear" );
    var_Destroy( p_intf, "fbosd-render" );
    var_Destroy( p_intf, "fbosd-display" );

    var_Destroy( p_intf, "fbosd-aspect-ratio" );

    CloseDisplay( p_intf );

510 511 512 513 514 515
    for( i = 0; i < FBOSD_RENDER_MAX; i++ )
    {
        free( p_sys->render[i].psz_string );
        p_sys->render[i].i_state = FBOSD_STATE_FREE;
    }

516
#if defined(FBOSD_BLENDING)
517 518 519
    if( p_sys->p_blend ) CloseBlending( p_intf );
#endif
    if( p_sys->p_text )  CloseTextRenderer( p_intf );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
520

521 522 523
    if( p_sys->p_image )
        image_HandlerDelete( p_sys->p_image );
    if( p_sys->p_overlay )
Laurent Aimar's avatar
Laurent Aimar committed
524
        picture_Release( p_sys->p_overlay );
525 526 527 528 529

    free( p_sys->p_style );
    free( p_sys );
}

530
#if defined(FBOSD_BLENDING)
531 532 533 534 535
static int OpenBlending( intf_thread_t *p_intf )
{
    if( p_intf->p_sys->p_blend ) return VLC_EGENERIC;

    p_intf->p_sys->p_blend =
536
            vlc_object_create( p_intf, sizeof(filter_t) );
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
    vlc_object_attach( p_intf->p_sys->p_blend, p_intf );
    p_intf->p_sys->p_blend->fmt_out.video.i_x_offset =
        p_intf->p_sys->p_blend->fmt_out.video.i_y_offset = 0;
    p_intf->p_sys->p_blend->fmt_out.video.i_aspect =
            p_intf->p_sys->fmt_out.i_aspect;
    p_intf->p_sys->p_blend->fmt_out.video.i_chroma =
            p_intf->p_sys->fmt_out.i_chroma;
    if( config_GetInt( p_intf, "freetype-yuvp" ) )
        p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
                VLC_FOURCC('Y','U','V','P');
    else
        p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
                VLC_FOURCC('Y','U','V','A');

    p_intf->p_sys->p_blend->p_module =
        module_Need( p_intf->p_sys->p_blend, "video blending", 0, 0 );

    if( !p_intf->p_sys->p_blend->p_module )
        return VLC_EGENERIC;

    return VLC_SUCCESS;
}

static void CloseBlending( intf_thread_t *p_intf )
{
    if( p_intf->p_sys->p_blend )
    {
        if( p_intf->p_sys->p_blend->p_module )
            module_Unneed( p_intf->p_sys->p_blend,
                           p_intf->p_sys->p_blend->p_module );

        vlc_object_detach( p_intf->p_sys->p_blend );
569
        vlc_object_release( p_intf->p_sys->p_blend );
570 571 572 573 574 575 576 577 578 579 580
    }
}
#endif

static int OpenTextRenderer( intf_thread_t *p_intf )
{
    char *psz_modulename = NULL;

    if( p_intf->p_sys->p_text ) return VLC_EGENERIC;

    p_intf->p_sys->p_text =
581
            vlc_object_create( p_intf, sizeof(filter_t) );
582 583 584 585 586 587 588 589 590 591 592 593 594 595
    vlc_object_attach( p_intf->p_sys->p_text, p_intf );

    p_intf->p_sys->p_text->fmt_out.video.i_width =
        p_intf->p_sys->p_text->fmt_out.video.i_visible_width =
        p_intf->p_sys->i_width;
    p_intf->p_sys->p_text->fmt_out.video.i_height =
        p_intf->p_sys->p_text->fmt_out.video.i_visible_height =
        p_intf->p_sys->i_height;

    psz_modulename = var_CreateGetString( p_intf, "text-renderer" );
    if( psz_modulename && *psz_modulename )
    {
        p_intf->p_sys->p_text->p_module =
            module_Need( p_intf->p_sys->p_text, "text renderer",
596
                            psz_modulename, true );
597 598 599 600 601 602
    }
    if( !p_intf->p_sys->p_text->p_module )
    {
        p_intf->p_sys->p_text->p_module =
            module_Need( p_intf->p_sys->p_text, "text renderer", 0, 0 );
    }
603
    free( psz_modulename );
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619

    if( !p_intf->p_sys->p_text->p_module )
        return VLC_EGENERIC;

    return VLC_SUCCESS;
}

static void CloseTextRenderer( intf_thread_t *p_intf )
{
    if( p_intf->p_sys->p_text )
    {
        if( p_intf->p_sys->p_text->p_module )
            module_Unneed( p_intf->p_sys->p_text,
                           p_intf->p_sys->p_text->p_module );

        vlc_object_detach( p_intf->p_sys->p_text );
620
        vlc_object_release( p_intf->p_sys->p_text );
621 622 623 624 625 626 627 628 629 630
    }
}

/*****************************************************************************
 * AllocatePicture:
 * allocate a picture buffer for use with the overlay fb.
 *****************************************************************************/
static picture_t *AllocatePicture( vlc_object_t *p_this,
                                   video_format_t *p_fmt )
{
Laurent Aimar's avatar
Laurent Aimar committed
631 632 633 634 635
    picture_t *p_picture = picture_New( p_fmt->i_chroma,
                                        p_fmt->i_width, p_fmt->i_height,
                                        p_fmt->i_aspect );
    if( !p_picture )
        return NULL;
636 637 638 639 640 641 642

    if( !p_fmt->p_palette &&
        ( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') ) )
    {
        p_fmt->p_palette = malloc( sizeof(video_palette_t) );
        if( !p_fmt->p_palette )
        {
Laurent Aimar's avatar
Laurent Aimar committed
643
            picture_Release( p_picture );
644 645 646
            return NULL;
        }
    }
Laurent Aimar's avatar
Laurent Aimar committed
647
    else
648
    {
Laurent Aimar's avatar
Laurent Aimar committed
649
        p_fmt->p_palette = NULL;
650
    }
Laurent Aimar's avatar
Laurent Aimar committed
651 652

    return p_picture;
653 654 655 656 657 658 659 660 661
}

/*****************************************************************************
 * DeAllocatePicture:
 * Deallocate a picture buffer and free all associated memory.
 *****************************************************************************/
static void DeAllocatePicture( vlc_object_t *p_this, picture_t *p_pic,
                               video_format_t *p_fmt )
{
662
    VLC_UNUSED(p_this);
Laurent Aimar's avatar
Laurent Aimar committed
663

664
    if( p_fmt )
665 666 667 668
    {
        free( p_fmt->p_palette );
        p_fmt->p_palette = NULL;
    }
Laurent Aimar's avatar
Laurent Aimar committed
669 670 671

    if( p_pic )
        picture_Release( p_pic );
672 673 674 675
}

/*****************************************************************************
 * SetOverlayTransparency: Set the transparency for this overlay fb,
676 677
 * - true is make transparent
 * - false is make non tranparent
678 679
 *****************************************************************************/
static void SetOverlayTransparency( intf_thread_t *p_intf,
680
                                    bool b_transparent )
681 682 683 684 685 686 687 688 689 690 691 692 693
{
    intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
    size_t i_size = p_sys->fmt_out.i_width * p_sys->fmt_out.i_height
                        * p_sys->i_bytes_per_pixel;
    size_t i_page_size = (p_sys->i_page_size > i_size) ?
                            i_size : p_sys->i_page_size;

    if( p_sys->p_overlay )
    {
        msg_Dbg( p_intf, "Make overlay %s",
                 b_transparent ? "transparent" : "opaque" );
        if( b_transparent )
            memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, i_page_size );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
694 695
        else
            memset( p_sys->p_overlay->p[0].p_pixels, 0x00, i_page_size );
696 697 698
    }
}

699
#if defined(FBOSD_BLENDING)
700 701 702 703 704 705 706 707 708 709 710 711 712 713
/*****************************************************************************
 * BlendPicture: Blend two pictures together..
 *****************************************************************************/
static int BlendPicture( intf_thread_t *p_intf, video_format_t *p_fmt_src,
                         video_format_t *p_fmt_dst, picture_t *p_pic_src,
                         picture_t *p_pic_dst )
{
    intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
    if( p_sys->p_blend && p_sys->p_blend->p_module )
    {
        int i_x_offset = p_sys->i_x;
        int i_y_offset = p_sys->i_y;

        memcpy( &p_sys->p_blend->fmt_in.video, p_fmt_src, sizeof( video_format_t ) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
714

715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
        /* Update the output picture size */
        p_sys->p_blend->fmt_out.video.i_width =
            p_sys->p_blend->fmt_out.video.i_visible_width =
                p_fmt_dst->i_width;
        p_sys->p_blend->fmt_out.video.i_height =
            p_sys->p_blend->fmt_out.video.i_visible_height =
                p_fmt_dst->i_height;

        i_x_offset = __MAX( i_x_offset, 0 );
        i_y_offset = __MAX( i_y_offset, 0 );

        p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_pic_dst,
            p_pic_src, p_pic_dst, i_x_offset, i_y_offset,
            p_sys->i_alpha );

        return VLC_SUCCESS;
    }
    return VLC_EGENERIC;
}

735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
static int InvertAlpha( intf_thread_t *p_intf, picture_t **p_pic, video_format_t fmt )
{
    uint8_t *p_begin = NULL, *p_end = NULL;
    uint8_t i_skip = 0;

    if( *p_pic && ((*p_pic)->i_planes != 1) )
    {
        msg_Err( p_intf,
                 "cannot invert alpha channel too many planes %d (only 1 supported)",
                 (*p_pic)->i_planes );
        return VLC_EGENERIC;
    }

    switch( fmt.i_chroma )
    {
        case VLC_FOURCC('R','V','2','4'):
            p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
            p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
                      ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
            i_skip = 3;
            break;
        case VLC_FOURCC('R','V','3','2'):
            p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
            p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
                      ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
            i_skip = 4;
            break;
        default:
            msg_Err( p_intf, "cannot invert alpha channel chroma not supported %4.4s",
                    (char *)&fmt.i_chroma );
            return VLC_EGENERIC;
    }

    for( ; p_begin < p_end; p_begin += i_skip )
    {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
770
        uint8_t i_opacity = 0;
771

Jean-Paul Saman's avatar
Jean-Paul Saman committed
772
        if( *p_begin != 0xFF )
773 774 775 776 777 778
            i_opacity = 255 - *p_begin;
        *p_begin = i_opacity;
    }
    /* end of kludge */
    return VLC_SUCCESS;
}
Jean-Paul Saman's avatar
Jean-Paul Saman committed
779
#endif
780

781 782 783 784 785 786 787 788 789
/*****************************************************************************
 * RenderPicture: Render the picture into the p_dest buffer.
 * We don't take transparent pixels into account, so we don't have to blend
 * the two images together.
 *****************************************************************************/
static int RenderPicture( intf_thread_t *p_intf, int i_x_offset, int i_y_offset,
                          picture_t *p_src, picture_t *p_dest )
{
    int i;
790
    VLC_UNUSED( p_intf );
791 792 793 794 795 796 797 798

    if( !p_dest && !p_src ) return VLC_EGENERIC;

    for( i = 0; i < p_src->i_planes ; i++ )
    {
        if( p_src->p[i].i_pitch == p_dest->p[i].i_pitch )
        {
            /* There are margins, but with the same width : perfect ! */
799 800
            vlc_memcpy( p_dest->p[i].p_pixels, p_src->p[i].p_pixels,
                        p_src->p[i].i_pitch * p_src->p[i].i_visible_lines );
801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
        }
        else
        {
            /* We need to proceed line by line */
            uint8_t *p_in  = p_src->p[i].p_pixels;
            uint8_t *p_out = p_dest->p[i].p_pixels;

            int i_x = i_x_offset * p_src->p[i].i_pixel_pitch;
            int i_x_clip, i_y_clip;

            /* Check boundaries, clip the image if necessary */
            i_x_clip = ( i_x + p_src->p[i].i_visible_pitch ) - p_dest->p[i].i_visible_pitch;
            i_x_clip = ( i_x_clip > 0 ) ? i_x_clip : 0;

            i_y_clip = ( i_y_offset + p_src->p[i].i_visible_lines ) - p_dest->p[i].i_visible_lines;
            i_y_clip = ( i_y_clip > 0 ) ? i_y_clip : 0;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
817
#if defined(FBOSD_DEBUG)
818 819 820 821 822 823 824 825 826 827 828 829
            msg_Dbg( p_intf, "i_pitch (%d,%d), (%d,%d)/(%d,%d)",
                     p_dest->p[i].i_visible_pitch, p_src->p[i].i_visible_pitch,
                     i_x_offset, i_y_offset, i_x, i_x_clip );
#endif
            if( ( i_y_offset <= p_dest->p[i].i_visible_lines ) &&
                ( i_x <= p_dest->p[i].i_visible_pitch ) )
            {
                int i_line;

                p_out += ( i_y_offset * p_dest->p[i].i_pitch );
                for( i_line = 0; i_line < ( p_src->p[i].i_visible_lines - i_y_clip ); i_line++ )
                {
830 831
                    vlc_memcpy( p_out + i_x, p_in,
                                p_src->p[i].i_visible_pitch - i_x_clip );
832 833 834 835 836 837 838 839 840 841 842 843
                    p_in += p_src->p[i].i_pitch;
                    p_out += p_dest->p[i].i_pitch;
                }
            }
        }
    }
    return VLC_SUCCESS;
}

/*****************************************************************************
 * RenderText - Render text to the desired picture format
 *****************************************************************************/
844 845
static picture_t *RenderText( intf_thread_t *p_intf, const char *psz_string,
                              text_style_t *p_style, video_format_t *p_fmt )
846 847 848 849 850
{
    intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
    subpicture_region_t *p_region;
    picture_t *p_dest = NULL;

851
    if( !psz_string ) return p_dest;
852 853 854 855 856 857 858 859 860

    if( p_sys->p_text && p_sys->p_text->p_module )
    {
        p_region = (subpicture_region_t *) malloc( sizeof(subpicture_region_t) );
        if( !p_region )
            return p_dest;

        memset( p_region, 0, sizeof(subpicture_region_t) );

861
        p_region->psz_text = strdup( psz_string );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
862 863 864 865 866
        if( !p_region->psz_text )
        {
            free( p_region );
            return NULL;
        }
867
        p_region->p_style = p_style;
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886

        p_region->fmt.i_chroma = VLC_FOURCC('T','E','X','T');
        p_region->fmt.i_aspect = 0;
        p_region->fmt.i_width = p_region->fmt.i_visible_width = 0;
        p_region->fmt.i_height = p_region->fmt.i_visible_height = 0;
        p_region->fmt.i_x_offset = 0;
        p_region->fmt.i_y_offset = 0;

        p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;

        if( p_sys->p_text->pf_render_text )
        {
            video_format_t fmt_out;

            memset( &fmt_out, 0, sizeof(video_format_t) );

            p_sys->p_text->pf_render_text( p_sys->p_text,
                                           p_region, p_region );

887
#if defined(FBOSD_BLENDING)
888 889
            fmt_out = p_region->fmt;
            fmt_out.i_bits_per_pixel = 32;
890
            vlc_memcpy( p_fmt, &fmt_out, sizeof(video_format_t) );
891

892 893 894 895 896
            p_dest = AllocatePicture( VLC_OBJECT(p_intf), &fmt_out );
            if( !p_dest )
            {
                if( p_region->picture.pf_release )
                    p_region->picture.pf_release( &p_region->picture );
897
                free( p_region->psz_text );
898 899 900 901
                free( p_region );
                return NULL;
            }
            vout_CopyPicture( VLC_OBJECT(p_intf), p_dest, &p_region->picture );
902 903 904 905
#else
            fmt_out.i_chroma = p_fmt->i_chroma;
            p_dest = ConvertImage( p_intf, &p_region->picture,
                                   &p_region->fmt, &fmt_out );
906 907 908
#endif
            if( p_region->picture.pf_release )
                p_region->picture.pf_release( &p_region->picture );
909
            free( p_region->psz_text );
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944
            free( p_region );
            return p_dest;
        }
        free( p_region->psz_text );
        free( p_region );
    }
    return p_dest;
}

/*****************************************************************************
 * LoadImage: Load an image from file into a picture buffer.
 *****************************************************************************/
static picture_t *LoadImage( intf_thread_t *p_intf, video_format_t *p_fmt,
                             char *psz_file )
{
    picture_t  *p_pic = NULL;

    if( psz_file && p_intf->p_sys->p_image )
    {
        video_format_t fmt_in, fmt_out;

        memset( &fmt_in, 0, sizeof(fmt_in) );
        memset( &fmt_out, 0, sizeof(fmt_out) );

        fmt_out.i_chroma = p_fmt->i_chroma;
        p_pic = image_ReadUrl( p_intf->p_sys->p_image, psz_file,
                               &fmt_in, &fmt_out );

        msg_Dbg( p_intf, "image size %dx%d chroma %4.4s",
                 fmt_out.i_width, fmt_out.i_height,
                 (char *)&p_fmt->i_chroma );
    }
    return p_pic;
}

945
#if ! defined(FBOSD_BLENDING)
946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
/*****************************************************************************
 * Convertmage: Convert image to another fourcc
 *****************************************************************************/
static picture_t *ConvertImage( intf_thread_t *p_intf, picture_t *p_pic,
                         video_format_t *p_fmt_in, video_format_t *p_fmt_out )
{
    intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
    picture_t  *p_old = NULL;

    if( p_sys->p_image )
    {
        p_old = image_Convert( p_sys->p_image, p_pic, p_fmt_in, p_fmt_out );

        msg_Dbg( p_intf, "converted image size %dx%d chroma %4.4s",
                 p_fmt_out->i_width, p_fmt_out->i_height,
                 (char *)&p_fmt_out->i_chroma );
    }
    return p_old;
}
#endif

/*****************************************************************************
 * Init: initialize framebuffer video thread output method
 *****************************************************************************/
static int Init( intf_thread_t *p_intf )
{
    intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;

    /* Initialize the output structure: RGB with square pixels, whatever
     * the input format is, since it's the only format we know */
    switch( p_sys->var_info.bits_per_pixel )
    {
    case 8: /* FIXME: set the palette */
        p_sys->fmt_out.i_chroma = VLC_FOURCC('R','G','B','2'); break;
    case 15:
        p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','5'); break;
    case 16:
        p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','6'); break;
    case 24:
        p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','2','4'); break;
    case 32:
        p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','3','2'); break;
    default:
        msg_Err( p_intf, "unknown screen depth %i",
                 p_sys->var_info.bits_per_pixel );
        return VLC_EGENERIC;
    }

    p_sys->fmt_out.i_bits_per_pixel = p_sys->var_info.bits_per_pixel;
    p_sys->fmt_out.i_width  = p_sys->i_width;
    p_sys->fmt_out.i_height = p_sys->i_height;

    /* Assume we have square pixels */
    if( p_sys->i_aspect < 0 )
    {
        p_sys->fmt_out.i_aspect = ( p_sys->i_width
                                  * VOUT_ASPECT_FACTOR ) / p_sys->i_height;
    }
    else p_sys->fmt_out.i_aspect = p_sys->i_aspect;

    p_sys->fmt_out.i_sar_num = p_sys->fmt_out.i_sar_den = 1;

    /* Allocate overlay buffer */
    p_sys->p_overlay = AllocatePicture( VLC_OBJECT(p_intf),
                                        &p_sys->fmt_out );
    if( !p_sys->p_overlay ) return VLC_EGENERIC;

1013
    SetOverlayTransparency( p_intf, true );
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047

    /* We know the chroma, allocate a buffer which will be used
     * to write to the overlay framebuffer */
    p_sys->p_overlay->p->i_pixel_pitch = p_sys->i_bytes_per_pixel;
    p_sys->p_overlay->p->i_lines = p_sys->var_info.yres;
    p_sys->p_overlay->p->i_visible_lines = p_sys->var_info.yres;

    if( p_sys->var_info.xres_virtual )
    {
        p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres_virtual
                             * p_sys->i_bytes_per_pixel;
    }
    else
    {
        p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres
                             * p_sys->i_bytes_per_pixel;
    }

    p_sys->p_overlay->p->i_visible_pitch = p_sys->var_info.xres
                                 * p_sys->i_bytes_per_pixel;

    p_sys->p_overlay->i_planes = 1;

    return VLC_SUCCESS;
}

/*****************************************************************************
 * End: terminate framebuffer interface
 *****************************************************************************/
static void End( intf_thread_t *p_intf )
{
    intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;

    /* CleanUp */
1048
    SetOverlayTransparency( p_intf, false );
1049 1050
    if( p_sys->p_overlay )
    {
1051
        int ret;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
1052 1053
        ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
                     p_sys->i_page_size );
1054 1055
        if( ret < 0 )
            msg_Err( p_intf, "unable to clear overlay" );
1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
    }

    DeAllocatePicture( VLC_OBJECT(p_intf), p_intf->p_sys->p_overlay,
                       &p_intf->p_sys->fmt_out );
    p_intf->p_sys->p_overlay = NULL;
}

/*****************************************************************************
 * OpenDisplay: initialize framebuffer
 *****************************************************************************/
static int OpenDisplay( intf_thread_t *p_intf )
{
    intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
    char *psz_device;                             /* framebuffer device path */
    struct fb_fix_screeninfo    fix_info;     /* framebuffer fix information */

    /* Open framebuffer device */
    if( !(psz_device = config_GetPsz( p_intf, "fbosd-dev" )) )
    {
        msg_Err( p_intf, "don't know which fb osd/overlay device to open" );
        return VLC_EGENERIC;
    }

    p_sys->i_fd = open( psz_device, O_RDWR );
    if( p_sys->i_fd == -1 )
    {
        msg_Err( p_intf, "cannot open %s (%s)", psz_device, strerror(errno) );
        free( psz_device );
        return VLC_EGENERIC;
    }
    free( psz_device );

    /* Get framebuffer device information */
    if( ioctl( p_sys->i_fd, FBIOGET_VSCREENINFO, &p_sys->var_info ) )
    {
        msg_Err( p_intf, "cannot get fb info (%s)", strerror(errno) );
        close( p_sys->i_fd );
        return VLC_EGENERIC;
    }

    /* Get some info on the framebuffer itself */
    if( ioctl( p_sys->i_fd, FBIOGET_FSCREENINFO, &fix_info ) == 0 )
    {
        p_sys->i_width = p_sys->fmt_out.i_width = p_sys->var_info.xres;
        p_sys->i_height = p_sys->fmt_out.i_height = p_sys->var_info.yres;
    }

    /* FIXME: if the image is full-size, it gets cropped on the left
     * because of the xres / xres_virtual slight difference */
    msg_Dbg( p_intf, "%ix%i (virtual %ix%i)",
             p_sys->var_info.xres, p_sys->var_info.yres,
             p_sys->var_info.xres_virtual,
             p_sys->var_info.yres_virtual );

    p_sys->fmt_out.i_width = p_sys->i_width;
    p_sys->fmt_out.i_height = p_sys->i_height;

    p_sys->p_palette = NULL;
    p_sys->b_pan = ( fix_info.ypanstep || fix_info.ywrapstep );

    switch( p_sys->var_info.bits_per_pixel )
    {
    case 8:
        p_sys->p_palette = malloc( 8 * 256 * sizeof( uint16_t ) );
        if( !p_sys->p_palette )
        {
            close( p_sys->i_fd );
            return VLC_ENOMEM;
        }
        p_sys->fb_cmap.start = 0;
        p_sys->fb_cmap.len = 256;
        p_sys->fb_cmap.red = p_sys->p_palette;
        p_sys->fb_cmap.green  = p_sys->p_palette + 256 * sizeof( uint16_t );
        p_sys->fb_cmap.blue   = p_sys->p_palette + 2 * 256 * sizeof( uint16_t );
        p_sys->fb_cmap.transp = p_sys->p_palette + 3 * 256 * sizeof( uint16_t );

        /* Save the colormap */
        ioctl( p_sys->i_fd, FBIOGETCMAP, &p_sys->fb_cmap );

        p_sys->i_bytes_per_pixel = 1;
        break;

    case 15:
    case 16:
        p_sys->i_bytes_per_pixel = 2;
        break;

    case 24:
        p_sys->i_bytes_per_pixel = 3;
        break;

    case 32:
        p_sys->i_bytes_per_pixel = 4;
        break;

    default:
        msg_Err( p_intf, "screen depth %d is not supported",
                         p_sys->var_info.bits_per_pixel );

        close( p_sys->i_fd );
        return VLC_EGENERIC;
    }

    p_sys->i_page_size = p_sys->i_width * p_sys->i_height
                         * p_sys->i_bytes_per_pixel;

    msg_Dbg( p_intf, "framebuffer type=%d, visual=%d, ypanstep=%d, "
             "ywrap=%d, accel=%d", fix_info.type, fix_info.visual,
             fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
    return VLC_SUCCESS;
}

/*****************************************************************************
 * CloseDisplay: terminate FB interface thread
 *****************************************************************************/
static void CloseDisplay( intf_thread_t *p_intf )
{
    intf_sys_t *p_sys = (intf_sys_t *) p_intf;

    /* Restore palette */
    if( p_sys->var_info.bits_per_pixel == 8 )
    {
        ioctl( p_sys->i_fd, FBIOPUTCMAP, &p_sys->fb_cmap );
        free( p_sys->p_palette );
        p_sys->p_palette = NULL;
    }

    /* Close fb */
    close( p_sys->i_fd );
}

1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201
static void Render( intf_thread_t *p_intf, struct fbosd_render_t *render )
{
    intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;

    if( render->i_state != FBOSD_STATE_RENDER ) return;
    if( !render->psz_string ) return;

    if( render->i_type == FBOSD_RENDER_IMAGE )
    {
        picture_t *p_pic;
        p_pic = LoadImage( p_intf, &p_sys->fmt_out, render->psz_string );
        if( p_pic )
        {
            RenderPicture( p_intf, render->i_x, render->i_y,
                           p_pic, p_sys->p_overlay );
Laurent Aimar's avatar
Laurent Aimar committed
1202
            picture_Release( p_pic );
1203 1204 1205 1206 1207
        }
    }
    else if( render->i_type == FBOSD_RENDER_TEXT )
    {
        picture_t *p_text;
1208
#if defined(FBOSD_BLENDING)
1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226
        video_format_t fmt_in;
        memset( &fmt_in, 0, sizeof(video_format_t) );
        p_text = RenderText( p_intf, render->psz_string, &render->text_style,
                             &fmt_in );
        if( p_text )
        {
            BlendPicture( p_intf, &fmt_in, &p_sys->fmt_out,
                          p_text, p_sys->p_overlay );
            msg_Dbg( p_intf, "releasing picture" );
            DeAllocatePicture( VLC_OBJECT( p_intf ), p_text, &fmt_in );
        }
#else
        p_text = RenderText( p_intf, render->psz_string, &render->text_style,
                             &p_sys->fmt_out );
        if( p_text )
        {
            RenderPicture( p_intf, render->i_x, render->i_y,
                           p_text, p_sys->p_overlay );
Laurent Aimar's avatar
Laurent Aimar committed
1227
            picture_Release( p_text );
1228 1229 1230 1231 1232 1233 1234 1235 1236
        }
#endif
    }
}

static void RenderClear( intf_thread_t *p_intf, struct fbosd_render_t *render )
{
    intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;

1237 1238
    vlc_memcpy( &render->text_style, &default_text_style,
                sizeof( text_style_t ) );
1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
    free( render->psz_string );
    render->psz_string = NULL;

    render->i_x = p_sys->i_x;
    render->i_y = p_sys->i_y;
    render->i_pos = p_sys->i_pos;
    render->i_alpha = p_sys->i_alpha;
    render->b_absolute = p_sys->b_absolute;
    render->i_state = FBOSD_STATE_FREE;
}

1250
static bool isRendererReady( intf_thread_t *p_intf )
1251 1252 1253 1254 1255 1256 1257 1258
{
    intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
    int i;

    /* Check if there are more items to render */
    for( i = 0; i < FBOSD_RENDER_MAX; i++ )
    {
        if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1259
            return false;
1260
    }
1261
    return true;
1262 1263
}

1264
/*****************************************************************************
Jean-Paul Saman's avatar
Jean-Paul Saman committed
1265
 * Run: thread
1266 1267 1268 1269 1270 1271 1272
 *****************************************************************************
 * This part of the interface is in a separate thread so that we can call
 * exec() from within it without annoying the rest of the program.
 *****************************************************************************/
static void Run( intf_thread_t *p_intf )
{
    intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1273
    int canc = vlc_savecancel();
1274 1275 1276

    while( !intf_ShouldDie( p_intf ) )
    {
1277
        int i;
1278

1279 1280 1281 1282
        /* Is there somthing to render? */
        for( i = 0; i < FBOSD_RENDER_MAX; i++ )
        {
            if( p_sys->render[i].i_state == FBOSD_STATE_RENDER )
1283
            {
1284 1285
                Render( p_intf, &p_sys->render[i] );
                RenderClear( p_intf, &p_sys->render[i] );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
1286 1287
            }
        }
1288 1289 1290

        if( p_sys->b_clear )
        {
1291
            SetOverlayTransparency( p_intf, true );
1292 1293 1294 1295

            var_SetString( p_intf, "fbosd-image", "" );
            var_SetString( p_intf, "fbosd-text", "" );

1296 1297
            p_sys->b_clear = false;
            p_sys->b_need_update = true;
1298 1299
        }

1300 1301
        if( p_sys->b_need_update && p_sys->p_overlay &&
            isRendererReady( p_intf ) )
1302
        {
1303
            int ret;
1304 1305 1306 1307
#if defined(FBOSD_BLENDING)
            /* Reverse alpha channel to work around FPGA bug */
            InvertAlpha( p_intf, &p_sys->p_overlay, p_sys->fmt_out );
#endif
1308 1309 1310 1311
            ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
                         p_sys->i_page_size );
            if( ret < 0 )
                msg_Err( p_intf, "unable to write to overlay" );
1312
            lseek( p_sys->i_fd, 0, SEEK_SET );
1313 1314 1315

            /* clear the picture */
            memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, p_sys->i_page_size );
1316
            p_sys->b_need_update = false;
1317 1318 1319 1320 1321
        }

        if( vlc_CPU() & CPU_CAPABILITY_FPU )
            msleep( INTF_IDLE_SLEEP );
        else
1322
            msleep( 500 );
1323 1324 1325
    }

    End( p_intf );
1326
    vlc_restorecancel( canc );
1327 1328 1329 1330 1331 1332 1333
}

static int OverlayCallback( vlc_object_t *p_this, char const *psz_cmd,
                 vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    intf_thread_t *p_intf = (intf_thread_t *) p_this;
    intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1334
    VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1335

1336
    if( !strncmp( psz_cmd, "fbosd-display", 13 ) )
1337
    {
1338
        p_sys->b_need_update = true;
1339 1340 1341
    }
    else if( !strncmp( psz_cmd, "fbosd-clear", 11 ) )
    {
1342 1343 1344 1345 1346 1347
        int i;
        /* Clear the entire render list */
        for( i = 0; i < FBOSD_RENDER_MAX; i++ )
        {
            RenderClear( p_intf, &p_sys->render[i] );
        }
1348
        p_sys->b_clear = true;
1349
    }
1350
    else if( !strncmp( psz_cmd, "fbosd-render", 12 ) )
1351
    {
1352 1353 1354 1355 1356 1357 1358 1359 1360 1361
        int i;
        /* Are we already busy with on slot ? */
        for( i = 0; i < FBOSD_RENDER_MAX; i++ )
        {
            if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
            {
                p_sys->render[i].i_state = FBOSD_STATE_RENDER;
                break;
            }
        }
1362
    }
1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401
    else
    {
        int i;
        /* Are we already busy with on slot ? */
        for( i = 0; i < FBOSD_RENDER_MAX; i++ )
        {
            if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
                break;
        }
        /* No, then find first FREE slot */
        if( p_sys->render[i].i_state != FBOSD_STATE_RESERVED )
        {
            for( i = 0; i < FBOSD_RENDER_MAX; i++ )
            {
                if( p_sys->render[i].i_state == FBOSD_STATE_FREE )
                    break;
            }
            if( p_sys->render[i].i_state != FBOSD_STATE_FREE )
            {
                msg_Warn( p_this, "render space depleated" );
                return VLC_SUCCESS;
            }
        }
        /* Found a free slot */
        p_sys->render[i].i_state = FBOSD_STATE_RESERVED;
        if( !strncmp( psz_cmd, "fbosd-image", 11 ) )
        {
            free( p_sys->render[i].psz_string );
            p_sys->render[i].psz_string = strdup( newval.psz_string );
            p_sys->render[i].i_type = FBOSD_RENDER_IMAGE;
        }
        else if( !strncmp( psz_cmd, "fbosd-text", 10 ) )
        {
            free( p_sys->render[i].psz_string );
            p_sys->render[i].psz_string = strdup( newval.psz_string );
            p_sys->render[i].i_type = FBOSD_RENDER_TEXT;
        }
        else if( !strncmp( psz_cmd, "fbosd-x", 7 ) )
        {
1402
            p_sys->render[i].b_absolute = false;
1403 1404 1405 1406 1407
            p_sys->render[i].i_x = (newval.i_int < p_sys->i_width) ?
                                    newval.i_int : p_sys->i_width;
        }
        else if( !strncmp( psz_cmd, "fbosd-y", 7 ) )
        {
1408
            p_sys->render[i].b_absolute = false;
1409 1410 1411 1412 1413
            p_sys->render[i].i_y = (newval.i_int < p_sys->i_height) ?
                                    newval.i_int : p_sys->i_height;
        }
        else if( !strncmp( psz_cmd, "fbosd-position", 14 ) )
        {
1414
            p_sys->render[i].b_absolute = true;
1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433
            p_sys->render[i].