X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fcodec%2Fkate.c;h=dc91302fedd6e5eb3bbfd460b5d1a023845de121;hb=9bca33b943a8e79c19644a593da8a67440814811;hp=29b3b2003f57dfe5eb14d4bde1541dce14d21f7f;hpb=f93b233d65ebe77ab284d978253e68baf0e02550;p=vlc diff --git a/modules/codec/kate.c b/modules/codec/kate.c index 29b3b2003f..dc91302fed 100644 --- a/modules/codec/kate.c +++ b/modules/codec/kate.c @@ -1,7 +1,7 @@ /***************************************************************************** * 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 @@ -35,10 +35,30 @@ #include #include +#ifdef HAVE_TIGER +# include +#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,35 +193,146 @@ 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 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" ) -vlc_module_begin(); - set_shortname( N_("Kate")); - set_description( N_("Kate text subtitles decoder") ); - 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") ); - set_capability( "packetizer", 100 ); - set_callbacks( OpenPacketizer, CloseDecoder ); - add_shortcut( "kate" ); + add_submodule () + set_description( N_("Kate text subtitles packetizer") ) + set_capability( "packetizer", 100 ) + set_callbacks( OpenPacketizer, CloseDecoder ) + add_shortcut( "kate" ) #endif -#ifdef ENABLE_FORMATTING - add_bool( "kate-formatted", true, NULL, FORMAT_TEXT, FORMAT_LONGTEXT, - true ); -#endif -vlc_module_end(); +vlc_module_end () /***************************************************************************** * OpenDecoder: probe the decoder and return score @@ -141,14 +344,15 @@ static int OpenDecoder( vlc_object_t *p_this ) { decoder_t *p_dec = (decoder_t*)p_this; decoder_sys_t *p_sys; - - msg_Dbg( p_dec, "kate: OpenDecoder"); + int i_ret; if( p_dec->fmt_in.i_codec != VLC_FOURCC('k','a','t','e') ) { return VLC_EGENERIC; } + msg_Dbg( p_dec, "kate: OpenDecoder"); + /* Set callbacks */ p_dec->pf_decode_sub = (subpicture_t *(*)(decoder_t *, block_t **)) DecodeBlock; @@ -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; headeridxki.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; yi_lines; ++y ) + { + uint8_t *p_line = (uint8_t*)(p_plane->p_pixels + y*p_plane->i_pitch); + unsigned int x; + for( x=0; xp_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,26 +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; - kate_tracker kin; - bool tracker_valid = false; + int i_ret; - ret = kate_decode_packetin( &p_sys->k, p_kp ); - if( ret < 0 ) + if( !p_sys->b_ready ) { - msg_Err( p_dec, "Kate failed to decode packet: %d", ret ); + msg_Err( p_dec, "Cannot decode Kate packet, decoder not initialized" ); return NULL; } - ret = kate_decode_eventout( &p_sys->k, &ev ); - if( ret < 0 ) + i_ret = kate_decode_packetin( &p_sys->k, p_kp ); + if( i_ret < 0 ) { - msg_Err( p_dec, "Kate failed to retrieve event: %d", ret ); + msg_Err( p_dec, "Kate failed to decode packet: %d", i_ret ); return NULL; } - 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", i_ret ); + return NULL; + } + if( i_ret > 0 ) { /* no event to go with this packet, this is normal */ return NULL; @@ -577,13 +1249,68 @@ static subpicture_t *DecodePacket( decoder_t *p_dec, kate_packet *p_kp, block_t /* we have an event */ /* Get a new spu */ - p_spu = p_dec->pf_spu_buffer_new( p_dec ); + 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; @@ -591,35 +1318,33 @@ 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 */ memset( &fmt, 0, sizeof(video_format_t) ); fmt.i_chroma = VLC_FOURCC('Y','U','V','P'); @@ -627,107 +1352,99 @@ static subpicture_t *DecodePacket( decoder_t *p_dec, kate_packet *p_kp, block_t fmt.i_width = fmt.i_visible_width = ev->bitmap->width; fmt.i_height = fmt.i_visible_height = ev->bitmap->height; fmt.i_x_offset = fmt.i_y_offset = 0; + fmt.p_palette = &palette; + CreateKatePalette( fmt.p_palette, ev->palette ); - p_bitmap_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt ); + p_bitmap_region = subpicture_region_New( &fmt ); if( !p_bitmap_region ) { msg_Err( p_dec, "cannot allocate SPU region" ); - p_dec->pf_spu_buffer_del( p_dec, p_spu ); + decoder_DeleteSubpicture( p_dec, p_spu ); return NULL; } - /* create the palette */ - CreateKatePalette( fmt.p_palette, ev->palette ); - /* create the bitmap */ - CreateKateBitmap( &p_bitmap_region->picture, ev->bitmap ); + CreateKateBitmap( p_bitmap_region->p_picture, ev->bitmap ); 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'); fmt.i_aspect = 0; fmt.i_width = fmt.i_height = 0; fmt.i_x_offset = fmt.i_y_offset = 0; - p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt ); + p_spu->p_region = subpicture_region_New( &fmt ); if( !p_spu->p_region ) { msg_Err( p_dec, "cannot allocate SPU region" ); - p_dec->pf_spu_buffer_del( p_dec, p_spu ); + decoder_DeleteSubpicture( p_dec, p_spu ); return NULL; } 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) { p_bitmap_region->i_align = SUBPICTURE_ALIGN_BOTTOM; } - p_spu->i_x = 0; - p_spu->i_y = 10; + p_spu->p_region->i_x = 0; + p_spu->p_region->i_y = 10; /* override if tracker info present */ - if (tracker_valid) + if (b_tracker_valid) { - p_spu->i_flags = 0; if (kin.has.region) { - p_spu->i_x = kin.region_x; - p_spu->i_y = kin.region_y; + 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; } /***************************************************************************** - * ParseKateComments: FIXME should be done in demuxer + * ParseKateComments: *****************************************************************************/ static void ParseKateComments( decoder_t *p_dec ) { - input_thread_t *p_input = (input_thread_t *)p_dec->p_parent; char *psz_name, *psz_value, *psz_comment; int i = 0; - if( p_input->i_object_type != VLC_OBJECT_INPUT ) return; - while ( i < p_dec->p_sys->kc.comments ) { psz_comment = strdup( p_dec->p_sys->kc.user_comments[i] ); if( !psz_comment ) - { - msg_Warn( p_dec, "out of memory" ); break; - } psz_name = psz_comment; psz_value = strchr( psz_comment, '=' ); if( psz_value ) { *psz_value = '\0'; psz_value++; - input_Control( p_input, INPUT_ADD_INFO, _("Kate comment"), - psz_name, "%s", psz_value ); + + if( !p_dec->p_description ) + p_dec->p_description = vlc_meta_New(); + if( p_dec->p_description ) + vlc_meta_AddExtra( p_dec->p_description, psz_name, psz_value ); } free( psz_comment ); i++; @@ -740,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 );