Commit c93c7588 authored by ogg.k.ogg.k's avatar ogg.k.ogg.k Committed by Rémi Denis-Courmont

Support for rendering Kate streams with libtiger

libtiger is a rendering library for Kate streams based on Pango
and Cairo, and supports almost everything Kate streams can do.

There's a bit of wonkiness with rendering on regions as VLC then
blends them onto a subsampled YUV buffer, and colors can bleed
in a not so nice way. I've tried frobbing the blender in blend.c
and I got rid of the chroma bleeding, but at the cost of other
artifacts, so I'm leaving it out.

Also, I can't find a way to get the preferences dialog to call
a callback on my variables with a pointer to the decoder, so
changing the variables will not have effect till VLC is started
again.

Also includes the misc kate fixes from a previous patch, which
was not applied yet, but changes the same code as this patch
(and fixed a possible crash).

Note that I don't have a big endian host to check, so the RGBA
component flipping code is untested.
Signed-off-by: default avatarRémi Denis-Courmont <rdenis@simphalempin.com>
parent de0146e4
......@@ -3841,6 +3841,23 @@ AS_IF([test "${enable_kate}" != "no"], [
])
dnl
dnl tiger decoder plugin
dnl
AC_ARG_ENABLE(tiger,
[ --enable-tiger Tiger rendering library for Kate streams (default enabled)])
AS_IF([test "${enable_tiger}" != "no"], [
PKG_CHECK_MODULES(TIGER,[tiger >= 0.3.1], [
AC_DEFINE(HAVE_TIGER, 1, [Define if libtiger is available.])
VLC_ADD_CFLAGS([kate],[$TIGER_CFLAGS])
VLC_ADD_LIBS([kate],[$TIGER_LIBS]) ],[
AS_IF([test "x${enable_tiger}" != "x"], [
AC_MSG_ERROR([libtiger does not appear to be installed on your system.])
])
])
])
dnl
dnl Video plugins
dnl
......
/*****************************************************************************
* kate.c : a decoder for the kate bitstream format
*****************************************************************************
* Copyright (C) 2000-2006 the VideoLAN team
* Copyright (C) 2000-2008 the VideoLAN team
* $Id$
*
* Authors: Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
......@@ -35,10 +35,30 @@
#include <vlc_osd.h>
#include <kate/kate.h>
#ifdef HAVE_TIGER
# include <tiger/tiger.h>
#endif
/* #define ENABLE_PACKETIZER */
#define ENABLE_FORMATTING
#define ENABLE_BITMAPS
/* #define ENABLE_PROFILE */
#ifdef ENABLE_PROFILE
# define PROFILE_START(name) int64_t profile_start_##name = mdate()
# define PROFILE_STOP(name) fprintf( stderr, #name ": %f ms\n", (mdate() - profile_start_##name)/1000.0f )
#else
# define PROFILE_START(name) ((void)0)
# define PROFILE_STOP(name) ((void)0)
#endif
#define CHECK_TIGER_RET( statement ) \
do \
{ \
int i_ret = (statement); \
if( i_ret < 0 ) \
{ \
msg_Dbg( p_dec, "Error in " #statement ": %d", i_ret ); \
} \
} while( 0 )
/*****************************************************************************
* decoder_sys_t : decoder descriptor
......@@ -69,14 +89,66 @@ struct decoder_sys_t
*/
mtime_t i_pts;
/* decoder_sys_t is shared between decoder and spu units */
vlc_mutex_t lock;
int i_refcount;
#ifdef HAVE_TIGER
/*
* Tiger properties
*/
tiger_renderer *p_tr;
subpicture_t *p_spu_final;
mtime_t last_render_ts;
bool b_dirty;
uint32_t i_tiger_default_font_color;
uint32_t i_tiger_default_background_color;
tiger_font_effect e_tiger_default_font_effect;
double f_tiger_default_font_effect_strength;
char *psz_tiger_default_font_desc;
double f_tiger_quality;
#endif
/*
* Options
*/
#ifdef ENABLE_FORMATTING
bool b_formatted;
#endif
bool b_use_tiger;
};
struct subpicture_sys_t
{
decoder_sys_t *p_dec_sys;
mtime_t i_start;
};
/*
* This is a global list of existing decoders.
* The reason for this list is that:
* - I want to be able to reconfigure Tiger when user prefs change
* - User prefs are variables which are not specific to a decoder (eg, if
* there are several decoders, there will still be one set of variables)
* - A callback set on those will not be passed a pointer to the decoder
* (as the decoder isn't known, and there could be multiple ones)
* - Creating variables in the decoder will create different ones, with
* values copied from the relevant user pref, so a callback on those
* won't be called when the user pref is changed
* Therefore, each decoder will register/unregister itself with this list,
* callbacks will be set for the user prefs, and these will in turn walk
* through this list and tell each decoder to update the relevant variable.
* HOWEVER.
* VLC's variable system is still in my way as I can't get the value of
* the user pref variables at decoder start *unless* I create my own vars
* which inherit from the user ones, but those are utterly useless after
* that first use, since they'll be detached from the ones the user can
* change. So, I create them, read their values, and then destroy them.
*/
static vlc_mutex_t kate_decoder_list_mutex = VLC_STATIC_MUTEX;
static size_t kate_decoder_list_size = 0;
static decoder_t **kate_decoder_list = NULL;
/*****************************************************************************
* Local prototypes
*****************************************************************************/
......@@ -93,6 +165,26 @@ static subpicture_t *ProcessPacket( decoder_t *p_dec, kate_packet *p_kp,
static subpicture_t *DecodePacket( decoder_t *p_dec, kate_packet *p_kp,
block_t *p_block );
static void ParseKateComments( decoder_t * );
static subpicture_t *SetupSimpleKateSPU( decoder_t *p_dec, subpicture_t *p_spu,
const kate_event *ev );
static void DecSysRelease( decoder_sys_t *p_sys );
static void DecSysHold( decoder_sys_t *p_sys );
#ifdef HAVE_TIGER
static uint32_t GetTigerColor( decoder_t *p_dec, const char *psz_prefix );
static char *GetTigerString( decoder_t *p_dec, const char *psz_name );
static int GetTigerInteger( decoder_t *p_dec, const char *psz_name );
static double GetTigerFloat( decoder_t *p_dec, const char *psz_name );
static void UpdateTigerFontColor( decoder_t *p_dec );
static void UpdateTigerBackgroundColor( decoder_t *p_dec );
static void UpdateTigerFontEffect( decoder_t *p_dec );
static void UpdateTigerQuality( decoder_t *p_dec );
static void UpdateTigerFontDesc( decoder_t *p_dec );
static int TigerConfigurationCallback( vlc_object_t *p_this, const char *psz_var,
vlc_value_t oldvar, vlc_value_t newval,
void *p_data );
static int OnConfigurationChanged( decoder_t *p_dec, const char *psz_var,
vlc_value_t oldval, vlc_value_t newval);
#endif
#define DEFAULT_NAME "Default"
#define MAX_LINE 8192
......@@ -101,22 +193,137 @@ static void ParseKateComments( decoder_t * );
* Module descriptor.
*****************************************************************************/
#ifdef ENABLE_FORMATTING
#define FORMAT_TEXT N_("Formatted Subtitles")
#define FORMAT_LONGTEXT N_("Kate streams allow for text formatting. " \
"VLC partly implements this, but you can choose to disable all formatting.")
"VLC partly implements this, but you can choose to disable all formatting." \
"Note that this has no effect is rendering via Tiger is enabled.")
#ifdef HAVE_TIGER
static const tiger_font_effect pi_font_effects[] = { tiger_font_plain, tiger_font_shadow, tiger_font_outline };
static const char * const ppsz_font_effect_names[] = { N_("None"), N_("Shadow"), N_("Outline") };
/* nicked off freetype.c */
static const int pi_color_values[] = {
0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
static const char *const ppsz_color_descriptions[] = {
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") };
#define TIGER_TEXT N_("Use Tiger for rendering")
#define TIGER_LONGTEXT N_("Kate streams can be rendered using the Tiger library. " \
"Disabling this will only render static text and bitmap based streams.")
#define TIGER_QUALITY_DEFAULT 1.0
#define TIGER_QUALITY_TEXT N_("Rendering quality")
#define TIGER_QUALITY_LONGTEXT N_("Select rendering quality, at the expense of speed. " \
"0 is fastest, 1 is highest quality.")
#define TIGER_DEFAULT_FONT_EFFECT_DEFAULT 0
#define TIGER_DEFAULT_FONT_EFFECT_TEXT N_("Default font effect")
#define TIGER_DEFAULT_FONT_EFFECT_LONGTEXT N_("Add a font effect to text to improve readability " \
"against different backgrounds.")
#define TIGER_DEFAULT_FONT_EFFECT_STRENGTH_DEFAULT 0.5
#define TIGER_DEFAULT_FONT_EFFECT_STRENGTH_TEXT N_("Default font effect strength")
#define TIGER_DEFAULT_FONT_EFFECT_STRENGTH_LONGTEXT N_("How pronounced to make the chosen font effect " \
"(effect dependent).")
#define TIGER_DEFAULT_FONT_DESC_DEFAULT ""
#define TIGER_DEFAULT_FONT_DESC_TEXT N_("Default font description")
#define TIGER_DEFAULT_FONT_DESC_LONGTEXT N_("Which font description to use if the Kate stream " \
"does not specify particular font parameters (name, size, etc) to use. " \
"A blank name will let Tiger choose font parameters where appropriate.")
#define TIGER_DEFAULT_FONT_COLOR_DEFAULT 0x00ffffff
#define TIGER_DEFAULT_FONT_COLOR_TEXT N_("Default font color")
#define TIGER_DEFAULT_FONT_COLOR_LONGTEXT N_("Default font color to use if the Kate stream " \
"does not specify a particular font color to use.")
#define TIGER_DEFAULT_FONT_ALPHA_DEFAULT 0xff
#define TIGER_DEFAULT_FONT_ALPHA_TEXT N_("Default font alpha")
#define TIGER_DEFAULT_FONT_ALPHA_LONGTEXT N_("Transparency of the default font color if the Kate stream " \
"does not specify a particular font color to use.")
#define TIGER_DEFAULT_BACKGROUND_COLOR_DEFAULT 0x00ffffff
#define TIGER_DEFAULT_BACKGROUND_COLOR_TEXT N_("Default background color")
#define TIGER_DEFAULT_BACKGROUND_COLOR_LONGTEXT N_("Default background color if the Kate stream " \
"does not specify a background color to use.")
#define TIGER_DEFAULT_BACKGROUND_ALPHA_DEFAULT 0
#define TIGER_DEFAULT_BACKGROUND_ALPHA_TEXT N_("Default background alpha")
#define TIGER_DEFAULT_BACKGROUND_ALPHA_LONGTEXT N_("Transparency of the default background color if the Kate stream " \
"does not specify a particular background color to use.")
#endif
#define HELP_TEXT N_( \
"Kate is a codec for text and image based overlays.\n" \
"The Tiger rendering library is needed to render complex Kate streams, " \
"but VLC can still render static text and image based subtitles if " \
"it is not available.\n" \
"Note that changing settings below will not take effect until a new stream is played. " \
"This will hopefully be fixed soon." \
)
vlc_module_begin ()
set_shortname( N_("Kate"))
set_description( N_("Kate text subtitles decoder") )
set_description( N_("Kate overlay decoder") )
set_help( HELP_TEXT )
set_capability( "decoder", 50 )
set_callbacks( OpenDecoder, CloseDecoder )
set_category( CAT_INPUT )
set_subcategory( SUBCAT_INPUT_SCODEC )
add_shortcut( "kate" )
add_bool( "kate-formatted", true, NULL, FORMAT_TEXT, FORMAT_LONGTEXT,
true );
#ifdef HAVE_TIGER
add_bool( "kate-use-tiger", true, NULL, TIGER_TEXT, TIGER_LONGTEXT,
true );
add_float_with_range( "kate-tiger-quality",
TIGER_QUALITY_DEFAULT, 0.0f, 1.0f, TigerConfigurationCallback,
TIGER_QUALITY_TEXT, TIGER_QUALITY_LONGTEXT,
true );
set_section( N_("Tiger rendering defaults"), NULL );
add_string( "kate-tiger-default-font-desc",
TIGER_DEFAULT_FONT_DESC_DEFAULT, TigerConfigurationCallback,
TIGER_DEFAULT_FONT_DESC_TEXT, TIGER_DEFAULT_FONT_DESC_LONGTEXT, true);
add_integer_with_range( "kate-tiger-default-font-effect",
TIGER_DEFAULT_FONT_EFFECT_DEFAULT,
0, sizeof(pi_font_effects)/sizeof(pi_font_effects[0])-1, TigerConfigurationCallback,
TIGER_DEFAULT_FONT_EFFECT_TEXT, TIGER_DEFAULT_FONT_EFFECT_LONGTEXT,
true );
change_integer_list( pi_font_effects, ppsz_font_effect_names, NULL );
add_float_with_range( "kate-tiger-default-font-effect-strength",
TIGER_DEFAULT_FONT_EFFECT_STRENGTH_DEFAULT, 0.0f, 1.0f, TigerConfigurationCallback,
TIGER_DEFAULT_FONT_EFFECT_STRENGTH_TEXT, TIGER_DEFAULT_FONT_EFFECT_STRENGTH_LONGTEXT,
true );
add_integer_with_range( "kate-tiger-default-font-color",
TIGER_DEFAULT_FONT_COLOR_DEFAULT, 0, 0x00ffffff, TigerConfigurationCallback,
TIGER_DEFAULT_FONT_COLOR_TEXT, TIGER_DEFAULT_FONT_COLOR_LONGTEXT,
true);
change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
add_integer_with_range( "kate-tiger-default-font-alpha",
TIGER_DEFAULT_FONT_ALPHA_DEFAULT, 0, 255, TigerConfigurationCallback,
TIGER_DEFAULT_FONT_ALPHA_TEXT, TIGER_DEFAULT_FONT_ALPHA_LONGTEXT,
true);
add_integer_with_range( "kate-tiger-default-background-color",
TIGER_DEFAULT_BACKGROUND_COLOR_DEFAULT, 0, 0x00ffffff, TigerConfigurationCallback,
TIGER_DEFAULT_BACKGROUND_COLOR_TEXT, TIGER_DEFAULT_BACKGROUND_COLOR_LONGTEXT,
true);
change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
add_integer_with_range( "kate-tiger-default-background-alpha",
TIGER_DEFAULT_BACKGROUND_ALPHA_DEFAULT, 0, 255, TigerConfigurationCallback,
TIGER_DEFAULT_BACKGROUND_ALPHA_TEXT, TIGER_DEFAULT_BACKGROUND_ALPHA_LONGTEXT,
true);
#endif
#ifdef ENABLE_PACKETIZER
add_submodule ()
set_description( N_("Kate text subtitles packetizer") )
......@@ -125,10 +332,6 @@ vlc_module_begin ()
add_shortcut( "kate" )
#endif
#ifdef ENABLE_FORMATTING
add_bool( "kate-formatted", true, NULL, FORMAT_TEXT, FORMAT_LONGTEXT,
true );
#endif
vlc_module_end ()
/*****************************************************************************
......@@ -141,6 +344,7 @@ static int OpenDecoder( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*)p_this;
decoder_sys_t *p_sys;
int i_ret;
msg_Dbg( p_dec, "kate: OpenDecoder");
......@@ -160,6 +364,10 @@ static int OpenDecoder( vlc_object_t *p_this )
(decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL )
return VLC_ENOMEM;
vlc_mutex_init( &p_sys->lock );
p_sys->i_refcount = 0;
DecSysHold( p_sys );
/* init of p_sys */
#ifdef ENABLE_PACKETIZER
p_sys->b_packetizer = false;
......@@ -174,10 +382,60 @@ static int OpenDecoder( vlc_object_t *p_this )
p_sys->i_headers = 0;
/* retrieve options */
#ifdef ENABLE_FORMATTING
p_sys->b_formatted = var_CreateGetBool( p_dec, "kate-formatted" );
vlc_mutex_lock( &kate_decoder_list_mutex );
#ifdef HAVE_TIGER
p_sys->b_use_tiger = var_CreateGetBool( p_dec, "kate-use-tiger" );
p_sys->p_tr = NULL;
p_sys->last_render_ts = 0;
/* get initial value of configuration */
p_sys->i_tiger_default_font_color = GetTigerColor( p_dec, "kate-tiger-default-font" );
p_sys->i_tiger_default_background_color = GetTigerColor( p_dec, "kate-tiger-default-background" );
p_sys->e_tiger_default_font_effect = GetTigerInteger( p_dec, "kate-tiger-default-font-effect" );
p_sys->f_tiger_default_font_effect_strength = GetTigerFloat( p_dec, "kate-tiger-default-font-effect-strength" );
p_sys->psz_tiger_default_font_desc = GetTigerString( p_dec, "kate-tiger-default-font-desc" );
p_sys->f_tiger_quality = GetTigerFloat( p_dec, "kate-tiger-quality" );
if( p_sys->b_use_tiger )
{
i_ret = tiger_renderer_create( &p_sys->p_tr );
if( i_ret < 0 )
{
msg_Warn ( p_dec, "Failed to create Tiger renderer, falling back to basic rendering" );
p_sys->p_tr = NULL;
p_sys->b_use_tiger = false;
}
CHECK_TIGER_RET( tiger_renderer_set_surface_clear_color( p_sys->p_tr, 1, 0, 0, 0, 0 ) );
UpdateTigerFontEffect( p_dec );
UpdateTigerFontColor( p_dec );
UpdateTigerBackgroundColor( p_dec );
UpdateTigerQuality( p_dec );
UpdateTigerFontDesc( p_dec );
}
#else
p_sys->b_use_tiger = false;
#endif
/* add the decoder to the global list */
decoder_t **list = ( decoder_t** ) realloc( kate_decoder_list, (kate_decoder_list_size+1) * sizeof( decoder_t* ));
if( list )
{
list[ kate_decoder_list_size++ ] = p_dec;
kate_decoder_list = list;
}
vlc_mutex_unlock( &kate_decoder_list_mutex );
return VLC_SUCCESS;
}
......@@ -213,8 +471,24 @@ static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
return NULL;
p_block = *pp_block;
if( p_block->i_rate != 0 )
p_block->i_length = p_block->i_length * p_block->i_rate / INPUT_RATE_DEFAULT;
if( p_block->i_flags & (BLOCK_FLAG_CORRUPTED) )
{
block_Release( p_block );
return NULL;
}
if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY) )
{
#ifdef HAVE_TIGER
/* Hmm, should we wait before flushing the renderer ? I think not, but not certain... */
vlc_mutex_lock( &p_sys->lock );
tiger_renderer_seek( p_sys->p_tr, 0 );
vlc_mutex_unlock( &p_sys->lock );
#endif
block_Release( p_block );
return NULL;
}
/* Block to Kate packet */
kate_packet_wrap(&kp, p_block->i_buffer, p_block->p_buffer);
......@@ -269,8 +543,8 @@ static int ProcessHeaders( decoder_t *p_dec )
kate_packet kp;
uint8_t *p_extra;
int i_extra;
int headeridx;
int ret;
int i_headeridx;
int i_ret;
if( !p_dec->fmt_in.i_extra ) return VLC_EGENERIC;
......@@ -293,10 +567,10 @@ static int ProcessHeaders( decoder_t *p_dec )
return VLC_EGENERIC;
}
ret = kate_decode_headerin( &p_sys->ki, &p_sys->kc, &kp );
if( ret < 0 )
i_ret = kate_decode_headerin( &p_sys->ki, &p_sys->kc, &kp );
if( i_ret < 0 )
{
msg_Err( p_dec, "this bitstream does not contain Kate data (%d)", ret );
msg_Err( p_dec, "this bitstream does not contain Kate data (%d)", i_ret );
return VLC_EGENERIC;
}
......@@ -306,7 +580,7 @@ static int ProcessHeaders( decoder_t *p_dec )
p_sys->ki.granule_shift);
/* parse all remaining header packets */
for (headeridx=1; headeridx<p_sys->ki.num_headers; ++headeridx)
for( i_headeridx = 1; i_headeridx < p_sys->ki.num_headers; ++i_headeridx )
{
kp.nbytes = *(p_extra++) << 8;
kp.nbytes |= (*(p_extra++) & 0xFF);
......@@ -315,19 +589,19 @@ static int ProcessHeaders( decoder_t *p_dec )
i_extra -= (kp.nbytes + 2);
if( i_extra < 0 )
{
msg_Err( p_dec, "header %d data corrupted", headeridx);
msg_Err( p_dec, "header %d data corrupted", i_headeridx );
return VLC_EGENERIC;
}
ret = kate_decode_headerin( &p_sys->ki, &p_sys->kc, &kp );
if( ret < 0 )
i_ret = kate_decode_headerin( &p_sys->ki, &p_sys->kc, &kp );
if( i_ret < 0 )
{
msg_Err( p_dec, "Kate header %d is corrupted: %d", headeridx, ret);
msg_Err( p_dec, "Kate header %d is corrupted: %d", i_headeridx, i_ret );
return VLC_EGENERIC;
}
/* header 1 is comments */
if( headeridx == 1 )
if( i_headeridx == 1 )
{
ParseKateComments( p_dec );
}
......@@ -339,10 +613,10 @@ static int ProcessHeaders( decoder_t *p_dec )
{
/* We have all the headers, initialize decoder */
msg_Dbg( p_dec, "we have all headers, initialize libkate for decoding" );
ret = kate_decode_init( &p_sys->k, &p_sys->ki );
if (ret < 0)
i_ret = kate_decode_init( &p_sys->k, &p_sys->ki );
if (i_ret < 0)
{
msg_Err( p_dec, "Kate failed to initialize for decoding: %d", ret );
msg_Err( p_dec, "Kate failed to initialize for decoding: %d", i_ret );
return VLC_EGENERIC;
}
p_sys->b_ready = true;
......@@ -395,7 +669,7 @@ static subpicture_t *ProcessPacket( decoder_t *p_dec, kate_packet *p_kp,
else
#endif
{
if( p_sys->i_headers >= p_sys->i_num_headers )
if( p_sys->i_headers >= p_sys->i_num_headers && p_sys->i_num_headers > 0)
p_buf = DecodePacket( p_dec, p_kp, p_block );
else
p_buf = NULL;
......@@ -406,7 +680,6 @@ static subpicture_t *ProcessPacket( decoder_t *p_dec, kate_packet *p_kp,
return p_buf;
}
#ifdef ENABLE_BITMAPS
/* nicked off blend.c */
static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
int r, int g, int b )
......@@ -415,7 +688,6 @@ static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
*u = ( ( -38 * r - 74 * g + 112 * b + 128 ) >> 8 ) + 128 ;
*v = ( ( 112 * r - 94 * g - 18 * b + 128 ) >> 8 ) + 128 ;
}
#endif
/*
This retrieves the size of the video.
......@@ -471,8 +743,6 @@ static void GetVideoSize( decoder_t *p_dec, int *w, int *h )
#endif
}
#ifdef ENABLE_BITMAPS
static void CreateKateBitmap( picture_t *pic, const kate_bitmap *bitmap )
{
size_t y;
......@@ -500,8 +770,6 @@ static void CreateKatePalette( video_palette_t *fmt_palette, const kate_palette
}
}
#endif
static void SetupText( decoder_t *p_dec, subpicture_t *p_spu, const kate_event *ev )
{
decoder_sys_t *p_sys = p_dec->p_sys;
......@@ -541,6 +809,408 @@ static void SetupText( decoder_t *p_dec, subpicture_t *p_spu, const kate_event *
}
}
#ifdef HAVE_TIGER
static void TigerDestroySubpicture( subpicture_t *p_subpic )
{
DecSysRelease( p_subpic->p_sys->p_dec_sys );
}
static void SubpictureReleaseRegions( subpicture_t *p_subpic )
{
if( p_subpic->p_region)
{
subpicture_region_ChainDelete( p_subpic->p_region );
p_subpic->p_region = NULL;
}
}
static void TigerPreRender( spu_t *p_spu, subpicture_t *p_subpic, const video_format_t *p_fmt )
{
decoder_sys_t *p_sys = p_subpic->p_sys->p_dec_sys;
VLC_UNUSED( p_spu );
VLC_UNUSED( p_fmt );
p_sys->p_spu_final = p_subpic;
}
/*
* We get premultiplied alpha, but VLC doesn't expect this, so we demultiply
* alpha to avoid double multiply (and thus thinner text than we should)).
* Best would be to have VLC be able to handle premultiplied alpha, as it
* would be faster to blend as well.
*
* Also, we flip color components around for big endian machines, as Tiger
* outputs ARGB or ABGR (the one we selected here) in host endianness.
*/
static void PostprocessTigerImage( plane_t *p_plane, unsigned int i_width )
{
PROFILE_START( tiger_renderer_postprocess );
int y;
for( y=0; y<p_plane->i_lines; ++y )
{
uint8_t *p_line = (uint8_t*)(p_plane->p_pixels + y*p_plane->i_pitch);
unsigned int x;
for( x=0; x<i_width; ++x )
{
uint8_t *p_pixel = p_line+x*4;
#ifdef WORDS_BIGENDIAN
uint8_t a = p_pixel[0];
#else
uint8_t a = p_pixel[3];
#endif
if( a )
{
#ifdef WORDS_BIGENDIAN
uint8_t tmp = pixel[2];
p_pixel[0] = p_pixel[3] * 255 / a;
p_pixel[3] = a;
p_pixel[2] = p_pixel[1] * 255 / a;
p_pixel[1] = tmp * 255 / a;
#else
p_pixel[0] = p_pixel[0] * 255 / a;
p_pixel[1] = p_pixel[1] * 255 / a;
p_pixel[2] = p_pixel[2] * 255 / a;
#endif
}
else
{
p_pixel[0] = 0;
p_pixel[1] = 0;
p_pixel[2] = 0;
p_pixel[3] = 0;
}
}
}
PROFILE_STOP( tiger_renderer_postprocess );
}
/* Tiger renders can end up looking a bit crap since they get overlaid on top of
a subsampled YUV image, so there can be a fair amount of chroma bleeding.
Looks good with white though since it's all luma. Hopefully that will be the
common case. */
static void TigerUpdateRegions( spu_t *p_spu, subpicture_t *p_subpic, const video_format_t *p_fmt, mtime_t ts )
{
decoder_sys_t *p_sys = p_subpic->p_sys->p_dec_sys;
subpicture_region_t *p_r;
video_format_t fmt;
plane_t *p_plane;
kate_float t;
int i_ret;
VLC_UNUSED( p_spu );
PROFILE_START( TigerUpdateRegions );
/* do not render more than once per frame, libtiger renders all events at once */
if (ts <= p_sys->last_render_ts)
{
SubpictureReleaseRegions( p_subpic );
return;
}
/* remember what frame we've rendered already */
p_sys->last_render_ts = ts;
if( p_subpic != p_sys->p_spu_final )
{
SubpictureReleaseRegions( p_subpic );
return;
}
/* time in seconds from the start of the stream */
t = (p_subpic->p_sys->i_start + ts - p_subpic->i_start ) / 1000000.0f;
/* it is likely that the current region (if any) can be kept as is; test for this */
vlc_mutex_lock( &p_sys->lock );
if( p_subpic->p_region && !p_sys->b_dirty && !tiger_renderer_is_dirty( p_sys->p_tr ))
{
PROFILE_START( tiger_renderer_update1 );
i_ret = tiger_renderer_update( p_sys->p_tr, t, 1 );
PROFILE_STOP( tiger_renderer_update1 );
if( i_ret < 0 )
{
SubpictureReleaseRegions( p_subpic );
vlc_mutex_unlock( &p_sys->lock );
return;
}
if( !tiger_renderer_is_dirty( p_sys->p_tr ) )
{
/* we can keep the current region list */
PROFILE_STOP( TigerUpdateRegions );
vlc_mutex_unlock( &p_sys->lock );
return;
}
}
vlc_mutex_unlock( &p_sys->lock );
/* we have to render again, reset current region list */
SubpictureReleaseRegions( p_subpic );
/* create a full frame region - this will also tell Tiger the size of the frame */
fmt = *p_fmt;
fmt.i_chroma = VLC_FOURCC('R','G','B','A');
fmt.i_width = fmt.i_visible_width;
fmt.i_height = fmt.i_visible_height;
fmt.i_bits_per_pixel = 0;
fmt.i_x_offset = fmt.i_y_offset = 0;
p_r = subpicture_region_New( &fmt );
if( !p_r )
{
return;
}
p_r->i_x = 0;
p_r->i_y = 0;
p_r->i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT;
vlc_mutex_lock( &p_sys->lock );
p_plane = &p_r->p_picture->p[0];
i_ret = tiger_renderer_set_buffer( p_sys->p_tr, p_plane->p_pixels, fmt.i_width, p_plane->i_lines, p_plane->i_pitch, 1 );
if( i_ret < 0 )
{
goto failure;
}
PROFILE_START( tiger_renderer_update );
i_ret = tiger_renderer_update( p_sys->p_tr, t, 1 );
if( i_ret < 0 )
{
goto failure;
}
PROFILE_STOP( tiger_renderer_update );
PROFILE_START( tiger_renderer_render );
i_ret = tiger_renderer_render( p_sys->p_tr );
if( i_ret < 0 )
{
goto failure;
}
PROFILE_STOP( tiger_renderer_render );
PostprocessTigerImage( p_plane, fmt.i_width );
p_subpic->p_region = p_r;
p_sys->b_dirty = false;
PROFILE_STOP( TigerUpdateRegions );
vlc_mutex_unlock( &p_sys->lock );
return;
failure:
vlc_mutex_unlock( &p_sys->lock );
subpicture_region_ChainDelete( p_r );
}
static uint32_t GetTigerColor( decoder_t *p_dec, const char *psz_prefix )
{
char *psz_tmp;
uint32_t i_color = 0;
if( asprintf( &psz_tmp, "%s-color", psz_prefix ) >= 0 )
{
uint32_t i_rgb = var_CreateGetInteger( p_dec, psz_tmp );
var_Destroy( p_dec, psz_tmp );
free( psz_tmp );
i_color |= i_rgb;
}
if( asprintf( &psz_tmp, "%s-alpha", psz_prefix ) >= 0 )
{
uint32_t i_alpha = var_CreateGetInteger( p_dec, psz_tmp );
var_Destroy( p_dec, psz_tmp );
free( psz_tmp );
i_color |= (i_alpha << 24);
}
return i_color;
}
static char *GetTigerString( decoder_t *p_dec, const char *psz_name )
{
char *psz_value = var_CreateGetString( p_dec, psz_name );
if( psz_value)
{
psz_value = strdup( psz_value );
}
var_Destroy( p_dec, psz_name );
return psz_value;
}
static int GetTigerInteger( decoder_t *p_dec, const char *psz_name )
{
int i_value = var_CreateGetInteger( p_dec, psz_name );
var_Destroy( p_dec, psz_name );
return i_value;
}
static double GetTigerFloat( decoder_t *p_dec, const char *psz_name )
{
double f_value = var_CreateGetFloat( p_dec, psz_name );
var_Destroy( p_dec, psz_name );
return f_value;
}
static void UpdateTigerQuality( decoder_t *p_dec )
{
decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
CHECK_TIGER_RET( tiger_renderer_set_quality( p_sys->p_tr, p_sys->f_tiger_quality ) );
p_sys->b_dirty = true;
}
static void UpdateTigerFontDesc( decoder_t *p_dec )
{
decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
CHECK_TIGER_RET( tiger_renderer_set_default_font_description( p_sys->p_tr, p_sys->psz_tiger_default_font_desc ) );
p_sys->b_dirty = true;
}
static void UpdateTigerFontColor( decoder_t *p_dec )
{
decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
double f_a = ((p_sys->i_tiger_default_font_color >> 24) & 0xff) / 255.0;
double f_r = ((p_sys->i_tiger_default_font_color >> 16) & 0xff) / 255.0;
double f_g = ((p_sys->i_tiger_default_font_color >> 8) & 0xff) / 255.0;
double f_b = (p_sys->i_tiger_default_font_color & 0xff) / 255.0;
CHECK_TIGER_RET( tiger_renderer_set_default_font_color( p_sys->p_tr, f_r, f_g, f_b, f_a ) );
p_sys->b_dirty = true;
}
static void UpdateTigerBackgroundColor( decoder_t *p_dec )
{
decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
double f_a = ((p_sys->i_tiger_default_background_color >> 24) & 0xff) / 255.0;
double f_r = ((p_sys->i_tiger_default_background_color >> 16) & 0xff) / 255.0;
double f_g = ((p_sys->i_tiger_default_background_color >> 8) & 0xff) / 255.0;
double f_b = (p_sys->i_tiger_default_background_color & 0xff) / 255.0;
CHECK_TIGER_RET( tiger_renderer_set_default_background_fill_color( p_sys->p_tr, f_r, f_g, f_b, f_a ) );
p_sys->b_dirty = true;
}
static void UpdateTigerFontEffect( decoder_t *p_dec )
{
decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
CHECK_TIGER_RET( tiger_renderer_set_default_font_effect( p_sys->p_tr,
p_sys->e_tiger_default_font_effect,
p_sys->f_tiger_default_font_effect_strength ) );
p_sys->b_dirty = true;
}
static int OnConfigurationChanged( decoder_t *p_dec, const char *psz_var,
vlc_value_t oldval, vlc_value_t newval )
{
decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
VLC_UNUSED( oldval );
vlc_mutex_lock( &p_sys->lock );
msg_Dbg( p_dec, "OnConfigurationChanged: %s\n", psz_var );
if( !p_sys->b_use_tiger || !p_sys->p_tr )
{
vlc_mutex_unlock( &p_sys->lock );
return VLC_SUCCESS;
}
#define TEST_TIGER_VAR( name ) \
if( !strcmp( name, psz_var ) )
TEST_TIGER_VAR( "kate-tiger-quality" )
{
p_sys->f_tiger_quality = newval.f_float;
UpdateTigerQuality( p_dec );
}
TEST_TIGER_VAR( "kate-tiger-default-font-desc" )
{
if( p_sys->psz_tiger_default_font_desc )
{
free( p_sys->psz_tiger_default_font_desc );
p_sys->psz_tiger_default_font_desc = NULL;
}
if( newval.psz_string )
{
p_sys->psz_tiger_default_font_desc = strdup( newval.psz_string );
}
UpdateTigerFontDesc( p_dec );
}
TEST_TIGER_VAR( "kate-tiger-default-font-color" )
{
p_sys->i_tiger_default_font_color = (p_sys->i_tiger_default_font_color & 0xff00000000) | newval.i_int;
UpdateTigerFontColor( p_dec );
}
TEST_TIGER_VAR( "kate-tiger-default-font-alpha" )
{
p_sys->i_tiger_default_font_color = (p_sys->i_tiger_default_font_color & 0x00ffffff) | (newval.i_int<<24);
UpdateTigerFontColor( p_dec );
}
TEST_TIGER_VAR( "kate-tiger-default-background-color" )
{
p_sys->i_tiger_default_background_color = (p_sys->i_tiger_default_background_color & 0xff00000000) | newval.i_int;
UpdateTigerBackgroundColor( p_dec );
}
TEST_TIGER_VAR( "kate-tiger-default-background-alpha" )
{
p_sys->i_tiger_default_background_color = (p_sys->i_tiger_default_background_color & 0x00ffffff) | (newval.i_int<<24);
UpdateTigerBackgroundColor( p_dec );
}
TEST_TIGER_VAR( "kate-tiger-default-font-effect" )
{
p_sys->e_tiger_default_font_effect = (tiger_font_effect)newval.i_int;
UpdateTigerFontEffect( p_dec );
}
TEST_TIGER_VAR( "kate-tiger-default-font-effect-strength" )
{
p_sys->f_tiger_default_font_effect_strength = newval.f_float;
UpdateTigerFontEffect( p_dec );
}
#undef TEST_TIGER_VAR
vlc_mutex_unlock( &p_sys->lock );
return VLC_SUCCESS;
}
static int TigerConfigurationCallback( vlc_object_t *p_this, const char *psz_var,
vlc_value_t oldval, vlc_value_t newval,
void *p_data )
{
size_t i_idx;
VLC_UNUSED( p_this );
VLC_UNUSED( oldval );
VLC_UNUSED( newval );
VLC_UNUSED( p_data );
vlc_mutex_lock( &kate_decoder_list_mutex );
/* Update all existing decoders from the global user prefs */
for( i_idx = 0; i_idx < kate_decoder_list_size; i_idx++ )
{
decoder_t *p_dec = kate_decoder_list[ i_idx ];
OnConfigurationChanged( p_dec, psz_var, oldval, newval );
}
vlc_mutex_unlock( &kate_decoder_list_mutex );
return VLC_SUCCESS;
}
#endif
/*****************************************************************************
* DecodePacket: decodes a Kate packet.
*****************************************************************************/
......@@ -549,27 +1219,28 @@ static subpicture_t *DecodePacket( decoder_t *p_dec, kate_packet *p_kp, block_t
decoder_sys_t *p_sys = p_dec->p_sys;
const kate_event *ev = NULL;
subpicture_t *p_spu = NULL;
subpicture_region_t *p_bitmap_region = NULL;
int ret;
video_format_t fmt;
video_format_t palette;
kate_tracker kin;
bool tracker_valid = false;
int i_ret;
if( !p_sys->b_ready )
{
msg_Err( p_dec, "Cannot decode Kate packet, decoder not initialized" );
return NULL;
}
ret = kate_decode_packetin( &p_sys->k, p_kp );
if( ret < 0 )
i_ret = kate_decode_packetin( &p_sys->k, p_kp );
if( i_ret < 0 )
{
msg_Err( p_dec, "Kate failed to decode packet: %d", ret );
msg_Err( p_dec, "Kate failed to decode packet: %d", i_ret );
return NULL;
}
ret = kate_decode_eventout( &p_sys->k, &ev );
if( ret < 0 )
i_ret = kate_decode_eventout( &p_sys->k, &ev );
if( i_ret < 0 )
{
msg_Err( p_dec, "Kate failed to retrieve event: %d", ret );
msg_Err( p_dec, "Kate failed to retrieve event: %d", i_ret );
return NULL;
}
if( ret > 0 )
if( i_ret > 0 )
{
/* no event to go with this packet, this is normal */
return NULL;
......@@ -581,10 +1252,65 @@ static subpicture_t *DecodePacket( decoder_t *p_dec, kate_packet *p_kp, block_t
p_spu = decoder_NewSubpicture( p_dec );
if( !p_spu )
{
msg_Err( p_dec, "Failed to allocate spu buffer" );
/* this will happen for lyrics as there is no vout - so no error */
/* msg_Err( p_dec, "Failed to allocate spu buffer" ); */
return NULL;
}
p_spu->i_start = p_block->i_pts;
p_spu->i_stop = p_block->i_pts + INT64_C(1000000)*ev->duration*p_sys->ki.gps_denominator/p_sys->ki.gps_numerator;
p_spu->b_ephemer = false;
p_spu->b_absolute = false;
#ifdef HAVE_TIGER
if( p_sys->b_use_tiger)
{
/* setup the structure to get our decoder struct back */
p_spu->p_sys = malloc( sizeof( subpicture_sys_t ));
if( !p_spu->p_sys )
{
decoder_DeleteSubpicture( p_dec, p_spu );
return NULL;
}
p_spu->p_sys->p_dec_sys = p_sys;
p_spu->p_sys->i_start = p_block->i_pts;
DecSysHold( p_sys );
p_spu->b_absolute = true;
/* add the event to tiger */
vlc_mutex_lock( &p_sys->lock );
CHECK_TIGER_RET( tiger_renderer_add_event( p_sys->p_tr, ev->ki, ev ) );
vlc_mutex_unlock( &p_sys->lock );
/* hookup render/update routines */
p_spu->pf_pre_render = TigerPreRender;
p_spu->pf_update_regions = TigerUpdateRegions;
p_spu->pf_destroy = TigerDestroySubpicture;
}
else
#endif
{
p_spu = SetupSimpleKateSPU( p_dec, p_spu, ev );
}
return p_spu;
}
/*****************************************************************************
* SetupSimpleKateSPU: creates text/bitmap regions where appropriate
*****************************************************************************/
static subpicture_t *SetupSimpleKateSPU( decoder_t *p_dec, subpicture_t *p_spu,
const kate_event *ev )
{
decoder_sys_t *p_sys = p_dec->p_sys;
video_format_t fmt;
subpicture_region_t *p_bitmap_region = NULL;
video_palette_t palette;
kate_tracker kin;
bool b_tracker_valid = false;
int i_ret;
/* these may be 0 for "not specified" */
p_spu->i_original_picture_width = p_sys->ki.original_canvas_width;
p_spu->i_original_picture_height = p_sys->ki.original_canvas_height;
......@@ -592,34 +1318,31 @@ static subpicture_t *DecodePacket( decoder_t *p_dec, kate_packet *p_kp, block_t
/* Create a new subpicture region */
memset( &fmt, 0, sizeof(video_format_t) );
#ifdef ENABLE_FORMATTING
if (p_sys->b_formatted)
{
ret = kate_tracker_init( &kin, &p_sys->ki, ev );
if( ret < 0)
i_ret = kate_tracker_init( &kin, &p_sys->ki, ev );
if( i_ret < 0)
{
msg_Err( p_dec, "failed to initialize kate tracker, event will be unformatted: %d", ret );
msg_Err( p_dec, "failed to initialize kate tracker, event will be unformatted: %d", i_ret );
}
else
{
int w = 720, h = 576; /* give sensible defaults just in case we fail to get the actual size */
GetVideoSize(p_dec, &w, &h);
ret = kate_tracker_update(&kin, 0, w, h, 0, 0, w, h);
if( ret < 0)
i_ret = kate_tracker_update(&kin, 0, w, h, 0, 0, w, h);
if( i_ret < 0)
{
kate_tracker_clear(&kin);
msg_Err( p_dec, "failed to update kate tracker, event will be unformatted: %d", ret );
msg_Err( p_dec, "failed to update kate tracker, event will be unformatted: %d", i_ret );
}
else
{
// TODO: parse tracker and set style, init fmt
tracker_valid = true;
b_tracker_valid = true;
}
}
}
#endif
#ifdef ENABLE_BITMAPS
if (ev->bitmap && ev->bitmap->type==kate_bitmap_type_paletted && ev->palette) {
/* create a separate region for the bitmap */
......@@ -645,7 +1368,6 @@ static subpicture_t *DecodePacket( decoder_t *p_dec, kate_packet *p_kp, block_t
msg_Dbg(p_dec, "Created bitmap, %zux%zu, %zu colors\n", ev->bitmap->width, ev->bitmap->height, ev->palette->ncolors);
}
#endif
/* text region */
fmt.i_chroma = VLC_FOURCC('T','E','X','T');
......@@ -662,11 +1384,6 @@ static subpicture_t *DecodePacket( decoder_t *p_dec, kate_packet *p_kp, block_t
SetupText( p_dec, p_spu, ev );
p_spu->i_start = p_block->i_pts;
p_spu->i_stop = p_block->i_pts + INT64_C(1000000)*ev->duration*p_sys->ki.gps_denominator/p_sys->ki.gps_numerator;
p_spu->b_ephemer = (p_block->i_length == 0);
p_spu->b_absolute = false;
/* default positioning */
p_spu->p_region->i_align = SUBPICTURE_ALIGN_BOTTOM;
if (p_bitmap_region)
......@@ -677,26 +1394,29 @@ static subpicture_t *DecodePacket( decoder_t *p_dec, kate_packet *p_kp, block_t
p_spu->p_region->i_y = 10;
/* override if tracker info present */
if (tracker_valid)
if (b_tracker_valid)
{
if (kin.has.region)
{
p_spu->p_region->i_x = kin.region_x;
p_spu->p_region->i_y = kin.region_y;
if (p_bitmap_region)
{
p_bitmap_region->i_x = kin.region_x;
p_bitmap_region->i_y = kin.region_y;
}
p_spu->b_absolute = true;
}
kate_tracker_clear(&kin);
}
#ifdef ENABLE_BITMAPS
/* if we have a bitmap, chain it before the text */
if (p_bitmap_region)
{
p_bitmap_region->p_next = p_spu->p_region;
p_spu->p_region = p_bitmap_region;
}
#endif
return p_spu;
}
......@@ -737,7 +1457,50 @@ static void ParseKateComments( decoder_t *p_dec )
static void CloseDecoder( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t *)p_this;
decoder_sys_t *p_sys = p_dec->p_sys;
size_t i_index;
/* remove the decoder from the global list */
vlc_mutex_lock( &kate_decoder_list_mutex );
for( i_index = 0; i_index < kate_decoder_list_size; i_index++ )
{
if( kate_decoder_list[ i_index ] == p_dec )
{
kate_decoder_list[ i_index ] = kate_decoder_list[ --kate_decoder_list_size ];
break;
}
}
vlc_mutex_unlock( &kate_decoder_list_mutex );
msg_Dbg( p_dec, "Closing Kate decoder" );
DecSysRelease( p_dec->p_sys );
}
static void DecSysHold( decoder_sys_t *p_sys )
{
vlc_mutex_lock( &p_sys->lock );
p_sys->i_refcount++;
vlc_mutex_unlock( &p_sys->lock );
}
static void DecSysRelease( decoder_sys_t *p_sys )
{
vlc_mutex_lock( &p_sys->lock );
p_sys->i_refcount--;
if( p_sys->i_refcount > 0)
{
vlc_mutex_unlock( &p_sys->lock );
return;
}
vlc_mutex_unlock( &p_sys->lock );
vlc_mutex_destroy( &p_sys->lock );
#ifdef HAVE_TIGER
if( p_sys->p_tr )
tiger_renderer_destroy( p_sys->p_tr );
if( p_sys->psz_tiger_default_font_desc )
free( p_sys->psz_tiger_default_font_desc );
#endif
if (p_sys->b_ready)
kate_clear( &p_sys->k );
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment