You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1433 lines
50 KiB
1433 lines
50 KiB
/*****************************************************************************
|
|
* fbosd.c : framebuffer osd plugin for vlc
|
|
*****************************************************************************
|
|
* Copyright (C) 2007-2008, the VideoLAN team
|
|
* $Id$
|
|
*
|
|
* 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
|
|
*****************************************************************************/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <vlc_common.h>
|
|
#include <vlc_plugin.h>
|
|
|
|
#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>
|
|
|
|
#undef FBOSD_BLENDING
|
|
#undef FBOSD_DEBUG
|
|
|
|
/*****************************************************************************
|
|
* 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 */
|
|
#if defined(FBOSD_BLENDING)
|
|
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 *,
|
|
bool );
|
|
static picture_t *LoadImage( intf_thread_t *, video_format_t *,
|
|
char * );
|
|
|
|
#if defined(FBOSD_BLENDING)
|
|
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 *,
|
|
text_style_t *, video_format_t * );
|
|
|
|
#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." )
|
|
|
|
static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
|
|
static const char *const ppsz_pos_descriptions[] =
|
|
{ N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
|
|
N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
|
|
|
|
static const int pi_color_values[] = {
|
|
0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
|
|
0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
|
|
0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
|
|
0x00000080, 0x000000FF, 0x0000FFFF};
|
|
static const char *const ppsz_color_descriptions[] = {
|
|
N_("Default"), N_("Black"),
|
|
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,
|
|
false );
|
|
add_string( "fbosd-aspect-ratio", "", NULL, ASPECT_RATIO_TEXT,
|
|
ASPECT_RATIO_LONGTEXT, true );
|
|
|
|
add_string( "fbosd-image", NULL, NULL, FBOSD_IMAGE_TEXT,
|
|
FBOSD_IMAGE_LONGTEXT, true );
|
|
add_string( "fbosd-text", NULL, NULL, FBOSD_TEXT,
|
|
FBOSD_LONGTEXT, true );
|
|
|
|
add_integer_with_range( "fbosd-alpha", 255, 0, 255, NULL, ALPHA_TEXT,
|
|
ALPHA_LONGTEXT, true );
|
|
|
|
set_section( N_("Position"), NULL );
|
|
add_integer( "fbosd-x", 0, NULL, POSX_TEXT,
|
|
POSX_LONGTEXT, false );
|
|
add_integer( "fbosd-y", 0, NULL, POSY_TEXT,
|
|
POSY_LONGTEXT, false );
|
|
add_integer( "fbosd-position", 8, NULL, POS_TEXT, POS_LONGTEXT, true );
|
|
change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
|
|
|
|
set_section( N_("Font"), NULL );
|
|
add_integer_with_range( "fbosd-font-opacity", 255, 0, 255, NULL,
|
|
OPACITY_TEXT, OPACITY_LONGTEXT, false );
|
|
add_integer( "fbosd-font-color", 0x00FFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
|
|
false );
|
|
change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
|
|
add_integer( "fbosd-font-size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT,
|
|
false );
|
|
|
|
set_section( N_("Commands"), NULL );
|
|
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 );
|
|
|
|
set_description( N_("GNU/Linux osd/overlay framebuffer interface") );
|
|
set_capability( "interface", 10 );
|
|
set_callbacks( Create, Destroy );
|
|
vlc_module_end();
|
|
|
|
/*****************************************************************************
|
|
* 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;
|
|
|
|
/* Position */
|
|
bool b_absolute;
|
|
int i_x;
|
|
int i_y;
|
|
int i_pos;
|
|
int i_alpha; /* transparency for images */
|
|
};
|
|
#define FBOSD_RENDER_MAX 10
|
|
|
|
/*****************************************************************************
|
|
* 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 */
|
|
bool b_pan; /* does device supports panning ? */
|
|
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;
|
|
#if defined(FBOSD_BLENDING)
|
|
filter_t *p_blend; /* alpha blending module */
|
|
#endif
|
|
filter_t *p_text; /* text renderer module */
|
|
|
|
/* Render */
|
|
struct fbosd_render_t render[FBOSD_RENDER_MAX];
|
|
|
|
/* Font style */
|
|
text_style_t *p_style; /* font control */
|
|
|
|
/* Position */
|
|
bool b_absolute;
|
|
int i_x;
|
|
int i_y;
|
|
int i_pos;
|
|
|
|
int i_alpha; /* transparency for images */
|
|
|
|
/* commands control */
|
|
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 */
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* 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;
|
|
char *psz_tmp;
|
|
int i;
|
|
|
|
/* 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;
|
|
}
|
|
vlc_memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ) );
|
|
|
|
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 );
|
|
|
|
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;
|
|
|
|
psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-image" );
|
|
var_AddCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
|
|
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 );
|
|
|
|
psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-text" );
|
|
var_AddCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
|
|
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 );
|
|
|
|
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 );
|
|
|
|
for( i = 0; i < FBOSD_RENDER_MAX; i++ )
|
|
{
|
|
vlc_memcpy( &p_sys->render[i].text_style, &default_text_style,
|
|
sizeof( text_style_t ) );
|
|
}
|
|
|
|
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 */
|
|
p_sys->b_absolute = true;
|
|
if( ( p_sys->i_x >= 0 ) && ( p_sys->i_y >= 0 ) )
|
|
{
|
|
p_sys->b_absolute = false;
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
/* Initialize framebuffer */
|
|
if( OpenDisplay( p_intf ) )
|
|
{
|
|
Destroy( VLC_OBJECT(p_intf) );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
Init( p_intf );
|
|
|
|
#if defined(FBOSD_BLENDING)
|
|
/* 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;
|
|
}
|
|
|
|
p_sys->b_render = true;
|
|
p_sys->b_need_update = true;
|
|
|
|
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;
|
|
int i;
|
|
|
|
p_sys->b_need_update = false;
|
|
p_sys->b_render = false;
|
|
p_sys->b_clear = false;
|
|
|
|
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 );
|
|
|
|
for( i = 0; i < FBOSD_RENDER_MAX; i++ )
|
|
{
|
|
free( p_sys->render[i].psz_string );
|
|
p_sys->render[i].i_state = FBOSD_STATE_FREE;
|
|
}
|
|
|
|
#if defined(FBOSD_BLENDING)
|
|
if( p_sys->p_blend ) CloseBlending( p_intf );
|
|
#endif
|
|
if( p_sys->p_text ) CloseTextRenderer( p_intf );
|
|
|
|
if( p_sys->p_image )
|
|
image_HandlerDelete( p_sys->p_image );
|
|
if( p_sys->p_overlay )
|
|
picture_Release( p_sys->p_overlay );
|
|
|
|
free( p_sys->p_style );
|
|
free( p_sys );
|
|
}
|
|
|
|
#if defined(FBOSD_BLENDING)
|
|
static int OpenBlending( intf_thread_t *p_intf )
|
|
{
|
|
if( p_intf->p_sys->p_blend ) return VLC_EGENERIC;
|
|
|
|
p_intf->p_sys->p_blend =
|
|
vlc_object_create( p_intf, sizeof(filter_t) );
|
|
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 );
|
|
vlc_object_release( p_intf->p_sys->p_blend );
|
|
}
|
|
}
|
|
#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 =
|
|
vlc_object_create( p_intf, sizeof(filter_t) );
|
|
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",
|
|
psz_modulename, true );
|
|
}
|
|
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 );
|
|
}
|
|
free( psz_modulename );
|
|
|
|
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 );
|
|
vlc_object_release( p_intf->p_sys->p_text );
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* 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 )
|
|
{
|
|
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;
|
|
|
|
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 )
|
|
{
|
|
picture_Release( p_picture );
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
p_fmt->p_palette = NULL;
|
|
}
|
|
|
|
return p_picture;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* 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 )
|
|
{
|
|
VLC_UNUSED(p_this);
|
|
|
|
if( p_fmt )
|
|
{
|
|
free( p_fmt->p_palette );
|
|
p_fmt->p_palette = NULL;
|
|
}
|
|
|
|
if( p_pic )
|
|
picture_Release( p_pic );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* SetOverlayTransparency: Set the transparency for this overlay fb,
|
|
* - true is make transparent
|
|
* - false is make non tranparent
|
|
*****************************************************************************/
|
|
static void SetOverlayTransparency( intf_thread_t *p_intf,
|
|
bool b_transparent )
|
|
{
|
|
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 );
|
|
else
|
|
memset( p_sys->p_overlay->p[0].p_pixels, 0x00, i_page_size );
|
|
}
|
|
}
|
|
|
|
#if defined(FBOSD_BLENDING)
|
|
/*****************************************************************************
|
|
* 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 ) );
|
|
|
|
/* 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;
|
|
}
|
|
|
|
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 )
|
|
{
|
|
uint8_t i_opacity = 0;
|
|
|
|
if( *p_begin != 0xFF )
|
|
i_opacity = 255 - *p_begin;
|
|
*p_begin = i_opacity;
|
|
}
|
|
/* end of kludge */
|
|
return VLC_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* 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;
|
|
VLC_UNUSED( p_intf );
|
|
|
|
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 ! */
|
|
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 );
|
|
}
|
|
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;
|
|
#if defined(FBOSD_DEBUG)
|
|
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++ )
|
|
{
|
|
vlc_memcpy( p_out + i_x, p_in,
|
|
p_src->p[i].i_visible_pitch - i_x_clip );
|
|
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
|
|
*****************************************************************************/
|
|
static picture_t *RenderText( intf_thread_t *p_intf, const char *psz_string,
|
|
text_style_t *p_style, video_format_t *p_fmt )
|
|
{
|
|
intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
|
|
subpicture_region_t *p_region;
|
|
picture_t *p_dest = NULL;
|
|
|
|
if( !psz_string ) return p_dest;
|
|
|
|
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) );
|
|
|
|
p_region->psz_text = strdup( psz_string );
|
|
if( !p_region->psz_text )
|
|
{
|
|
free( p_region );
|
|
return NULL;
|
|
}
|
|
p_region->p_style = p_style;
|
|
|
|
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 );
|
|
|
|
#if defined(FBOSD_BLENDING)
|
|
fmt_out = p_region->fmt;
|
|
fmt_out.i_bits_per_pixel = 32;
|
|
vlc_memcpy( p_fmt, &fmt_out, sizeof(video_format_t) );
|
|
|
|
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 );
|
|
free( p_region->psz_text );
|
|
free( p_region );
|
|
return NULL;
|
|
}
|
|
vout_CopyPicture( VLC_OBJECT(p_intf), p_dest, &p_region->picture );
|
|
#else
|
|
fmt_out.i_chroma = p_fmt->i_chroma;
|
|
p_dest = ConvertImage( p_intf, &p_region->picture,
|
|
&p_region->fmt, &fmt_out );
|
|
#endif
|
|
if( p_region->picture.pf_release )
|
|
p_region->picture.pf_release( &p_region->picture );
|
|
free( p_region->psz_text );
|
|
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;
|
|
}
|
|
|
|
#if ! defined(FBOSD_BLENDING)
|
|
/*****************************************************************************
|
|
* 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;
|
|
|
|
SetOverlayTransparency( p_intf, true );
|
|
|
|
/* 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 */
|
|
SetOverlayTransparency( p_intf, false );
|
|
if( p_sys->p_overlay )
|
|
{
|
|
int ret;
|
|
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 clear overlay" );
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
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 );
|
|
picture_Release( p_pic );
|
|
}
|
|
}
|
|
else if( render->i_type == FBOSD_RENDER_TEXT )
|
|
{
|
|
picture_t *p_text;
|
|
#if defined(FBOSD_BLENDING)
|
|
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 );
|
|
picture_Release( p_text );
|
|
}
|
|
#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;
|
|
|
|
vlc_memcpy( &render->text_style, &default_text_style,
|
|
sizeof( text_style_t ) );
|
|
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;
|
|
}
|
|
|
|
static bool isRendererReady( intf_thread_t *p_intf )
|
|
{
|
|
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 )
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Run: thread
|
|
*****************************************************************************
|
|
* 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;
|
|
|
|
while( !intf_ShouldDie( p_intf ) )
|
|
{
|
|
int i;
|
|
|
|
/* Is there somthing to render? */
|
|
for( i = 0; i < FBOSD_RENDER_MAX; i++ )
|
|
{
|
|
if( p_sys->render[i].i_state == FBOSD_STATE_RENDER )
|
|
{
|
|
Render( p_intf, &p_sys->render[i] );
|
|
RenderClear( p_intf, &p_sys->render[i] );
|
|
}
|
|
}
|
|
|
|
if( p_sys->b_clear )
|
|
{
|
|
SetOverlayTransparency( p_intf, true );
|
|
|
|
var_SetString( p_intf, "fbosd-image", "" );
|
|
var_SetString( p_intf, "fbosd-text", "" );
|
|
|
|
p_sys->b_clear = false;
|
|
p_sys->b_need_update = true;
|
|
}
|
|
|
|
if( p_sys->b_need_update && p_sys->p_overlay &&
|
|
isRendererReady( p_intf ) )
|
|
{
|
|
int ret;
|
|
#if defined(FBOSD_BLENDING)
|
|
/* Reverse alpha channel to work around FPGA bug */
|
|
InvertAlpha( p_intf, &p_sys->p_overlay, p_sys->fmt_out );
|
|
#endif
|
|
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" );
|
|
lseek( p_sys->i_fd, 0, SEEK_SET );
|
|
|
|
/* clear the picture */
|
|
memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, p_sys->i_page_size );
|
|
p_sys->b_need_update = false;
|
|
}
|
|
|
|
if( vlc_CPU() & CPU_CAPABILITY_FPU )
|
|
msleep( INTF_IDLE_SLEEP );
|
|
else
|
|
msleep( 500 );
|
|
}
|
|
|
|
End( p_intf );
|
|
}
|
|
|
|
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;
|
|
VLC_UNUSED(oldval); VLC_UNUSED(p_data);
|
|
|
|
if( !strncmp( psz_cmd, "fbosd-display", 13 ) )
|
|
{
|
|
p_sys->b_need_update = true;
|
|
}
|
|
else if( !strncmp( psz_cmd, "fbosd-clear", 11 ) )
|
|
{
|
|
int i;
|
|
/* Clear the entire render list */
|
|
for( i = 0; i < FBOSD_RENDER_MAX; i++ )
|
|
{
|
|
RenderClear( p_intf, &p_sys->render[i] );
|
|
}
|
|
p_sys->b_clear = true;
|
|
}
|
|
else if( !strncmp( psz_cmd, "fbosd-render", 12 ) )
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
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 ) )
|
|
{
|
|
p_sys->render[i].b_absolute = false;
|
|
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 ) )
|
|
{
|
|
p_sys->render[i].b_absolute = false;
|
|
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 ) )
|
|
{
|
|
p_sys->render[i].b_absolute = true;
|
|
p_sys->render[i].i_pos = newval.i_int;
|
|
}
|
|
else if( !strncmp( psz_cmd, "fbosd-font-size", 15 ) )
|
|
{
|
|
p_sys->render[i].text_style.i_font_size = newval.i_int;
|
|
}
|
|
else if( !strncmp( psz_cmd, "fbosd-font-color", 16 ) )
|
|
{
|
|
p_sys->render[i].text_style.i_font_color = newval.i_int;
|
|
}
|
|
else if( !strncmp( psz_cmd, "fbosd-font-opacity", 18 ) )
|
|
{
|
|
p_sys->render[i].text_style.i_font_alpha = 255 - newval.i_int;
|
|
}
|
|
else if( !strncmp( psz_cmd, "fbosd-alpha", 11 ) )
|
|
{
|
|
p_sys->render[i].i_alpha = newval.i_int;
|
|
}
|
|
}
|
|
return VLC_SUCCESS;
|
|
}
|
|
|