1 /*****************************************************************************
2 * kate.c : a decoder for the kate bitstream format
3 *****************************************************************************
4 * Copyright (C) 2000-2008 the VideoLAN team
7 * Authors: Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_input.h>
34 #include <vlc_codec.h>
37 #include <kate/kate.h>
39 # include <tiger/tiger.h>
42 /* #define ENABLE_PACKETIZER */
43 /* #define ENABLE_PROFILE */
46 # define PROFILE_START(name) int64_t profile_start_##name = mdate()
47 # define PROFILE_STOP(name) fprintf( stderr, #name ": %f ms\n", (mdate() - profile_start_##name)/1000.0f )
49 # define PROFILE_START(name) ((void)0)
50 # define PROFILE_STOP(name) ((void)0)
53 #define CHECK_TIGER_RET( statement ) \
56 int i_ret = (statement); \
59 msg_Dbg( p_dec, "Error in " #statement ": %d", i_ret ); \
63 /*****************************************************************************
64 * decoder_sys_t : decoder descriptor
65 *****************************************************************************/
68 #ifdef ENABLE_PACKETIZER
92 /* decoder_sys_t is shared between decoder and spu units */
100 tiger_renderer *p_tr;
101 subpicture_t *p_spu_final;
102 mtime_t last_render_ts;
105 uint32_t i_tiger_default_font_color;
106 uint32_t i_tiger_default_background_color;
107 tiger_font_effect e_tiger_default_font_effect;
108 double f_tiger_default_font_effect_strength;
109 char *psz_tiger_default_font_desc;
110 double f_tiger_quality;
120 struct subpicture_sys_t
122 decoder_sys_t *p_dec_sys;
128 * This is a global list of existing decoders.
129 * The reason for this list is that:
130 * - I want to be able to reconfigure Tiger when user prefs change
131 * - User prefs are variables which are not specific to a decoder (eg, if
132 * there are several decoders, there will still be one set of variables)
133 * - A callback set on those will not be passed a pointer to the decoder
134 * (as the decoder isn't known, and there could be multiple ones)
135 * - Creating variables in the decoder will create different ones, with
136 * values copied from the relevant user pref, so a callback on those
137 * won't be called when the user pref is changed
138 * Therefore, each decoder will register/unregister itself with this list,
139 * callbacks will be set for the user prefs, and these will in turn walk
140 * through this list and tell each decoder to update the relevant variable.
142 * VLC's variable system is still in my way as I can't get the value of
143 * the user pref variables at decoder start *unless* I create my own vars
144 * which inherit from the user ones, but those are utterly useless after
145 * that first use, since they'll be detached from the ones the user can
146 * change. So, I create them, read their values, and then destroy them.
148 static vlc_mutex_t kate_decoder_list_mutex = VLC_STATIC_MUTEX;
149 static size_t kate_decoder_list_size = 0;
150 static decoder_t **kate_decoder_list = NULL;
152 /*****************************************************************************
154 *****************************************************************************/
155 static int OpenDecoder ( vlc_object_t * );
156 static void CloseDecoder ( vlc_object_t * );
157 #ifdef ENABLE_PACKETIZER
158 static int OpenPacketizer( vlc_object_t *p_this );
161 static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block );
162 static int ProcessHeaders( decoder_t *p_dec );
163 static subpicture_t *ProcessPacket( decoder_t *p_dec, kate_packet *p_kp,
164 block_t **pp_block );
165 static subpicture_t *DecodePacket( decoder_t *p_dec, kate_packet *p_kp,
167 static void ParseKateComments( decoder_t * );
168 static subpicture_t *SetupSimpleKateSPU( decoder_t *p_dec, subpicture_t *p_spu,
169 const kate_event *ev );
170 static void DecSysRelease( decoder_sys_t *p_sys );
171 static void DecSysHold( decoder_sys_t *p_sys );
173 static uint32_t GetTigerColor( decoder_t *p_dec, const char *psz_prefix );
174 static char *GetTigerString( decoder_t *p_dec, const char *psz_name );
175 static int GetTigerInteger( decoder_t *p_dec, const char *psz_name );
176 static double GetTigerFloat( decoder_t *p_dec, const char *psz_name );
177 static void UpdateTigerFontColor( decoder_t *p_dec );
178 static void UpdateTigerBackgroundColor( decoder_t *p_dec );
179 static void UpdateTigerFontEffect( decoder_t *p_dec );
180 static void UpdateTigerQuality( decoder_t *p_dec );
181 static void UpdateTigerFontDesc( decoder_t *p_dec );
182 static int TigerConfigurationCallback( vlc_object_t *p_this, const char *psz_var,
183 vlc_value_t oldvar, vlc_value_t newval,
185 static int OnConfigurationChanged( decoder_t *p_dec, const char *psz_var,
186 vlc_value_t oldval, vlc_value_t newval);
189 #define DEFAULT_NAME "Default"
190 #define MAX_LINE 8192
192 /*****************************************************************************
194 *****************************************************************************/
196 #define FORMAT_TEXT N_("Formatted Subtitles")
197 #define FORMAT_LONGTEXT N_("Kate streams allow for text formatting. " \
198 "VLC partly implements this, but you can choose to disable all formatting." \
199 "Note that this has no effect is rendering via Tiger is enabled.")
203 static const tiger_font_effect pi_font_effects[] = { tiger_font_plain, tiger_font_shadow, tiger_font_outline };
204 static const char * const ppsz_font_effect_names[] = { N_("None"), N_("Shadow"), N_("Outline") };
206 /* nicked off freetype.c */
207 static const int pi_color_values[] = {
208 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
209 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
210 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
211 static const char *const ppsz_color_descriptions[] = {
212 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
213 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
214 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
216 #define TIGER_TEXT N_("Use Tiger for rendering")
217 #define TIGER_LONGTEXT N_("Kate streams can be rendered using the Tiger library. " \
218 "Disabling this will only render static text and bitmap based streams.")
220 #define TIGER_QUALITY_DEFAULT 1.0
221 #define TIGER_QUALITY_TEXT N_("Rendering quality")
222 #define TIGER_QUALITY_LONGTEXT N_("Select rendering quality, at the expense of speed. " \
223 "0 is fastest, 1 is highest quality.")
225 #define TIGER_DEFAULT_FONT_EFFECT_DEFAULT 0
226 #define TIGER_DEFAULT_FONT_EFFECT_TEXT N_("Default font effect")
227 #define TIGER_DEFAULT_FONT_EFFECT_LONGTEXT N_("Add a font effect to text to improve readability " \
228 "against different backgrounds.")
230 #define TIGER_DEFAULT_FONT_EFFECT_STRENGTH_DEFAULT 0.5
231 #define TIGER_DEFAULT_FONT_EFFECT_STRENGTH_TEXT N_("Default font effect strength")
232 #define TIGER_DEFAULT_FONT_EFFECT_STRENGTH_LONGTEXT N_("How pronounced to make the chosen font effect " \
233 "(effect dependent).")
235 #define TIGER_DEFAULT_FONT_DESC_DEFAULT ""
236 #define TIGER_DEFAULT_FONT_DESC_TEXT N_("Default font description")
237 #define TIGER_DEFAULT_FONT_DESC_LONGTEXT N_("Which font description to use if the Kate stream " \
238 "does not specify particular font parameters (name, size, etc) to use. " \
239 "A blank name will let Tiger choose font parameters where appropriate.")
241 #define TIGER_DEFAULT_FONT_COLOR_DEFAULT 0x00ffffff
242 #define TIGER_DEFAULT_FONT_COLOR_TEXT N_("Default font color")
243 #define TIGER_DEFAULT_FONT_COLOR_LONGTEXT N_("Default font color to use if the Kate stream " \
244 "does not specify a particular font color to use.")
246 #define TIGER_DEFAULT_FONT_ALPHA_DEFAULT 0xff
247 #define TIGER_DEFAULT_FONT_ALPHA_TEXT N_("Default font alpha")
248 #define TIGER_DEFAULT_FONT_ALPHA_LONGTEXT N_("Transparency of the default font color if the Kate stream " \
249 "does not specify a particular font color to use.")
251 #define TIGER_DEFAULT_BACKGROUND_COLOR_DEFAULT 0x00ffffff
252 #define TIGER_DEFAULT_BACKGROUND_COLOR_TEXT N_("Default background color")
253 #define TIGER_DEFAULT_BACKGROUND_COLOR_LONGTEXT N_("Default background color if the Kate stream " \
254 "does not specify a background color to use.")
256 #define TIGER_DEFAULT_BACKGROUND_ALPHA_DEFAULT 0
257 #define TIGER_DEFAULT_BACKGROUND_ALPHA_TEXT N_("Default background alpha")
258 #define TIGER_DEFAULT_BACKGROUND_ALPHA_LONGTEXT N_("Transparency of the default background color if the Kate stream " \
259 "does not specify a particular background color to use.")
263 #define HELP_TEXT N_( \
264 "Kate is a codec for text and image based overlays.\n" \
265 "The Tiger rendering library is needed to render complex Kate streams, " \
266 "but VLC can still render static text and image based subtitles if " \
267 "it is not available.\n" \
268 "Note that changing settings below will not take effect until a new stream is played. " \
269 "This will hopefully be fixed soon." \
273 set_shortname( N_("Kate"))
274 set_description( N_("Kate overlay decoder") )
275 set_help( HELP_TEXT )
276 set_capability( "decoder", 50 )
277 set_callbacks( OpenDecoder, CloseDecoder )
278 set_category( CAT_INPUT )
279 set_subcategory( SUBCAT_INPUT_SCODEC )
280 add_shortcut( "kate" )
282 add_bool( "kate-formatted", true, NULL, FORMAT_TEXT, FORMAT_LONGTEXT,
286 add_bool( "kate-use-tiger", true, NULL, TIGER_TEXT, TIGER_LONGTEXT,
288 add_float_with_range( "kate-tiger-quality",
289 TIGER_QUALITY_DEFAULT, 0.0f, 1.0f, TigerConfigurationCallback,
290 TIGER_QUALITY_TEXT, TIGER_QUALITY_LONGTEXT,
293 set_section( N_("Tiger rendering defaults"), NULL );
294 add_string( "kate-tiger-default-font-desc",
295 TIGER_DEFAULT_FONT_DESC_DEFAULT, TigerConfigurationCallback,
296 TIGER_DEFAULT_FONT_DESC_TEXT, TIGER_DEFAULT_FONT_DESC_LONGTEXT, true);
297 add_integer_with_range( "kate-tiger-default-font-effect",
298 TIGER_DEFAULT_FONT_EFFECT_DEFAULT,
299 0, sizeof(pi_font_effects)/sizeof(pi_font_effects[0])-1, TigerConfigurationCallback,
300 TIGER_DEFAULT_FONT_EFFECT_TEXT, TIGER_DEFAULT_FONT_EFFECT_LONGTEXT,
302 change_integer_list( pi_font_effects, ppsz_font_effect_names, NULL );
303 add_float_with_range( "kate-tiger-default-font-effect-strength",
304 TIGER_DEFAULT_FONT_EFFECT_STRENGTH_DEFAULT, 0.0f, 1.0f, TigerConfigurationCallback,
305 TIGER_DEFAULT_FONT_EFFECT_STRENGTH_TEXT, TIGER_DEFAULT_FONT_EFFECT_STRENGTH_LONGTEXT,
307 add_integer_with_range( "kate-tiger-default-font-color",
308 TIGER_DEFAULT_FONT_COLOR_DEFAULT, 0, 0x00ffffff, TigerConfigurationCallback,
309 TIGER_DEFAULT_FONT_COLOR_TEXT, TIGER_DEFAULT_FONT_COLOR_LONGTEXT,
311 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
312 add_integer_with_range( "kate-tiger-default-font-alpha",
313 TIGER_DEFAULT_FONT_ALPHA_DEFAULT, 0, 255, TigerConfigurationCallback,
314 TIGER_DEFAULT_FONT_ALPHA_TEXT, TIGER_DEFAULT_FONT_ALPHA_LONGTEXT,
316 add_integer_with_range( "kate-tiger-default-background-color",
317 TIGER_DEFAULT_BACKGROUND_COLOR_DEFAULT, 0, 0x00ffffff, TigerConfigurationCallback,
318 TIGER_DEFAULT_BACKGROUND_COLOR_TEXT, TIGER_DEFAULT_BACKGROUND_COLOR_LONGTEXT,
320 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL );
321 add_integer_with_range( "kate-tiger-default-background-alpha",
322 TIGER_DEFAULT_BACKGROUND_ALPHA_DEFAULT, 0, 255, TigerConfigurationCallback,
323 TIGER_DEFAULT_BACKGROUND_ALPHA_TEXT, TIGER_DEFAULT_BACKGROUND_ALPHA_LONGTEXT,
327 #ifdef ENABLE_PACKETIZER
329 set_description( N_("Kate text subtitles packetizer") )
330 set_capability( "packetizer", 100 )
331 set_callbacks( OpenPacketizer, CloseDecoder )
332 add_shortcut( "kate" )
337 /*****************************************************************************
338 * OpenDecoder: probe the decoder and return score
339 *****************************************************************************
340 * Tries to launch a decoder and return score so that the interface is able
342 *****************************************************************************/
343 static int OpenDecoder( vlc_object_t *p_this )
345 decoder_t *p_dec = (decoder_t*)p_this;
346 decoder_sys_t *p_sys;
349 if( p_dec->fmt_in.i_codec != VLC_FOURCC('k','a','t','e') )
354 msg_Dbg( p_dec, "kate: OpenDecoder");
357 p_dec->pf_decode_sub = (subpicture_t *(*)(decoder_t *, block_t **))
359 p_dec->pf_packetize = (block_t *(*)(decoder_t *, block_t **))
362 /* Allocate the memory needed to store the decoder's structure */
363 if( ( p_dec->p_sys = p_sys = malloc(sizeof(*p_sys)) ) == NULL )
366 vlc_mutex_init( &p_sys->lock );
367 p_sys->i_refcount = 0;
371 #ifdef ENABLE_PACKETIZER
372 p_sys->b_packetizer = false;
374 p_sys->b_ready = false;
377 kate_comment_init( &p_sys->kc );
378 kate_info_init( &p_sys->ki );
380 p_sys->i_num_headers = 0;
381 p_sys->i_headers = 0;
383 /* retrieve options */
384 p_sys->b_formatted = var_CreateGetBool( p_dec, "kate-formatted" );
386 vlc_mutex_lock( &kate_decoder_list_mutex );
390 p_sys->b_use_tiger = var_CreateGetBool( p_dec, "kate-use-tiger" );
393 p_sys->last_render_ts = 0;
395 /* get initial value of configuration */
396 p_sys->i_tiger_default_font_color = GetTigerColor( p_dec, "kate-tiger-default-font" );
397 p_sys->i_tiger_default_background_color = GetTigerColor( p_dec, "kate-tiger-default-background" );
398 p_sys->e_tiger_default_font_effect = GetTigerInteger( p_dec, "kate-tiger-default-font-effect" );
399 p_sys->f_tiger_default_font_effect_strength = GetTigerFloat( p_dec, "kate-tiger-default-font-effect-strength" );
400 p_sys->psz_tiger_default_font_desc = GetTigerString( p_dec, "kate-tiger-default-font-desc" );
401 p_sys->f_tiger_quality = GetTigerFloat( p_dec, "kate-tiger-quality" );
403 if( p_sys->b_use_tiger )
405 i_ret = tiger_renderer_create( &p_sys->p_tr );
408 msg_Warn ( p_dec, "Failed to create Tiger renderer, falling back to basic rendering" );
410 p_sys->b_use_tiger = false;
413 CHECK_TIGER_RET( tiger_renderer_set_surface_clear_color( p_sys->p_tr, 1, 0, 0, 0, 0 ) );
415 UpdateTigerFontEffect( p_dec );
416 UpdateTigerFontColor( p_dec );
417 UpdateTigerBackgroundColor( p_dec );
418 UpdateTigerQuality( p_dec );
419 UpdateTigerFontDesc( p_dec );
424 p_sys->b_use_tiger = false;
428 es_format_Init( &p_dec->fmt_out, SPU_ES, 0 );
430 /* add the decoder to the global list */
431 decoder_t **list = realloc( kate_decoder_list, (kate_decoder_list_size+1) * sizeof( *list ));
434 list[ kate_decoder_list_size++ ] = p_dec;
435 kate_decoder_list = list;
438 vlc_mutex_unlock( &kate_decoder_list_mutex );
443 #ifdef ENABLE_PACKETIZER
444 static int OpenPacketizer( vlc_object_t *p_this )
446 decoder_t *p_dec = (decoder_t*)p_this;
448 int i_ret = OpenDecoder( p_this );
450 if( i_ret == VLC_SUCCESS )
452 p_dec->p_sys->b_packetizer = true;
453 p_dec->fmt_out.i_codec = VLC_FOURCC( 'k', 'a', 't', 'e' );
460 /****************************************************************************
461 * DecodeBlock: the whole thing
462 ****************************************************************************
463 * This function must be fed with kate packets.
464 ****************************************************************************/
465 static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
467 decoder_sys_t *p_sys = p_dec->p_sys;
471 if( !pp_block || !*pp_block )
476 if( p_block->i_flags & (BLOCK_FLAG_CORRUPTED) )
478 block_Release( p_block );
482 if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY) )
485 /* Hmm, should we wait before flushing the renderer ? I think not, but not certain... */
486 vlc_mutex_lock( &p_sys->lock );
487 tiger_renderer_seek( p_sys->p_tr, 0 );
488 vlc_mutex_unlock( &p_sys->lock );
490 block_Release( p_block );
494 /* Block to Kate packet */
495 kate_packet_wrap(&kp, p_block->i_buffer, p_block->p_buffer);
497 if( p_sys->i_headers == 0 && p_dec->fmt_in.i_extra )
499 /* Headers already available as extra data */
500 p_sys->i_num_headers = ((unsigned char*)p_dec->fmt_in.p_extra)[0];
501 p_sys->i_headers = p_sys->i_num_headers;
503 else if( kp.nbytes && (p_sys->i_headers==0 || p_sys->i_headers < p_sys->ki.num_headers ))
505 /* Backup headers as extra data */
508 p_dec->fmt_in.p_extra =
509 realloc( p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra + kp.nbytes + 2 );
510 p_extra = (void*)(((unsigned char*)p_dec->fmt_in.p_extra) + p_dec->fmt_in.i_extra);
511 *(p_extra++) = kp.nbytes >> 8;
512 *(p_extra++) = kp.nbytes & 0xFF;
514 memcpy( p_extra, kp.data, kp.nbytes );
515 p_dec->fmt_in.i_extra += kp.nbytes + 2;
517 block_Release( *pp_block );
518 p_sys->i_num_headers = ((unsigned char*)p_dec->fmt_in.p_extra)[0];
523 if( p_sys->i_headers == p_sys->i_num_headers && p_sys->i_num_headers>0 )
525 if( ProcessHeaders( p_dec ) != VLC_SUCCESS )
527 p_sys->i_headers = 0;
528 p_dec->fmt_in.i_extra = 0;
529 block_Release( *pp_block );
532 else p_sys->i_headers++;
535 return ProcessPacket( p_dec, &kp, pp_block );
538 /*****************************************************************************
539 * ProcessHeaders: process Kate headers.
540 *****************************************************************************/
541 static int ProcessHeaders( decoder_t *p_dec )
543 decoder_sys_t *p_sys = p_dec->p_sys;
550 if( !p_dec->fmt_in.i_extra ) return VLC_EGENERIC;
552 p_extra = p_dec->fmt_in.p_extra;
553 i_extra = p_dec->fmt_in.i_extra;
555 /* skip number of headers */
559 /* Take care of the initial Kate header */
560 kp.nbytes = *(p_extra++) << 8;
561 kp.nbytes |= (*(p_extra++) & 0xFF);
563 p_extra += kp.nbytes;
564 i_extra -= (kp.nbytes + 2);
567 msg_Err( p_dec, "header data corrupted");
571 i_ret = kate_decode_headerin( &p_sys->ki, &p_sys->kc, &kp );
574 msg_Err( p_dec, "this bitstream does not contain Kate data (%d)", i_ret );
578 msg_Dbg( p_dec, "%s %s text, granule rate %f, granule shift %d",
579 p_sys->ki.language, p_sys->ki.category,
580 (double)p_sys->ki.gps_numerator/p_sys->ki.gps_denominator,
581 p_sys->ki.granule_shift);
583 /* parse all remaining header packets */
584 for( i_headeridx = 1; i_headeridx < p_sys->ki.num_headers; ++i_headeridx )
586 kp.nbytes = *(p_extra++) << 8;
587 kp.nbytes |= (*(p_extra++) & 0xFF);
589 p_extra += kp.nbytes;
590 i_extra -= (kp.nbytes + 2);
593 msg_Err( p_dec, "header %d data corrupted", i_headeridx );
597 i_ret = kate_decode_headerin( &p_sys->ki, &p_sys->kc, &kp );
600 msg_Err( p_dec, "Kate header %d is corrupted: %d", i_headeridx, i_ret );
604 /* header 1 is comments */
605 if( i_headeridx == 1 )
607 ParseKateComments( p_dec );
611 #ifdef ENABLE_PACKETIZER
612 if( !p_sys->b_packetizer )
615 /* We have all the headers, initialize decoder */
616 msg_Dbg( p_dec, "we have all headers, initialize libkate for decoding" );
617 i_ret = kate_decode_init( &p_sys->k, &p_sys->ki );
620 msg_Err( p_dec, "Kate failed to initialize for decoding: %d", i_ret );
623 p_sys->b_ready = true;
625 #ifdef ENABLE_PACKETIZER
628 p_dec->fmt_out.i_extra = p_dec->fmt_in.i_extra;
629 p_dec->fmt_out.p_extra =
630 realloc( p_dec->fmt_out.p_extra, p_dec->fmt_out.i_extra );
631 memcpy( p_dec->fmt_out.p_extra,
632 p_dec->fmt_in.p_extra, p_dec->fmt_out.i_extra );
639 /*****************************************************************************
640 * ProcessPacket: processes a kate packet.
641 *****************************************************************************/
642 static subpicture_t *ProcessPacket( decoder_t *p_dec, kate_packet *p_kp,
645 decoder_sys_t *p_sys = p_dec->p_sys;
646 block_t *p_block = *pp_block;
647 subpicture_t *p_buf = NULL;
649 /* Date management */
650 if( p_block->i_pts > 0 && p_block->i_pts != p_sys->i_pts )
652 p_sys->i_pts = p_block->i_pts;
655 *pp_block = NULL; /* To avoid being fed the same packet again */
657 #ifdef ENABLE_PACKETIZER
658 if( p_sys->b_packetizer )
660 /* Date management */
661 p_block->i_dts = p_block->i_pts = p_sys->i_pts;
663 if( p_sys->i_headers >= p_sys->i_num_headers )
664 p_block->i_length = p_sys->i_pts - p_block->i_pts;
666 p_block->i_length = 0;
673 if( p_sys->i_headers >= p_sys->i_num_headers && p_sys->i_num_headers > 0)
674 p_buf = DecodePacket( p_dec, p_kp, p_block );
678 if( p_block ) block_Release( p_block );
684 /* nicked off blend.c */
685 static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
686 int r, int g, int b )
688 *y = ( ( ( 66 * r + 129 * g + 25 * b + 128 ) >> 8 ) + 16 );
689 *u = ( ( -38 * r - 74 * g + 112 * b + 128 ) >> 8 ) + 128 ;
690 *v = ( ( 112 * r - 94 * g - 18 * b + 128 ) >> 8 ) + 128 ;
694 This retrieves the size of the video.
695 The best case is when the original video size is known, as we can then
696 scale images to match. In this case, since VLC autoscales, we want to
697 return the original size and let VLC scale everything.
698 if the original size is not known, then VLC can't resize, so we return
699 the size of the incoming video. If sizes in the Kate stream are in
700 relative units, it works fine. If they are absolute, you get what you
701 ask for. Images aren't rescaled.
703 static void GetVideoSize( decoder_t *p_dec, int *w, int *h )
705 /* searching for vout to get its size is frowned upon, so we don't and
706 use a default size if the original canvas size is not specified. */
708 decoder_sys_t *p_sys = p_dec->p_sys;
709 if( p_sys->ki.original_canvas_width > 0 && p_sys->ki.original_canvas_height > 0 )
711 *w = p_sys->ki.original_canvas_width;
712 *h = p_sys->ki.original_canvas_height;
713 msg_Dbg( p_dec, "original canvas %zu %zu",
714 p_sys->ki.original_canvas_width, p_sys->ki.original_canvas_height );
718 /* nothing, leave defaults */
719 msg_Dbg( p_dec, "original canvas size unknown");
722 /* keep this just in case it might be allowed one day ;) */
723 vout_thread_t *p_vout;
724 p_vout = vlc_object_find( (vlc_object_t*)p_dec, VLC_OBJECT_VOUT, FIND_CHILD );
727 decoder_sys_t *p_sys = p_dec->p_sys;
728 if( p_sys->ki.original_canvas_width > 0 && p_sys->ki.original_canvas_height > 0 )
730 *w = p_sys->ki.original_canvas_width;
731 *h = p_sys->ki.original_canvas_height;
735 *w = p_vout->fmt_in.i_width;
736 *h = p_vout->fmt_in.i_height;
738 msg_Dbg( p_dec, "video: in %d %d, out %d %d, original canvas %zu %zu",
739 p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
740 p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
741 p_sys->ki.original_canvas_width, p_sys->ki.original_canvas_height );
742 vlc_object_release( p_vout );
747 static void CreateKateBitmap( picture_t *pic, const kate_bitmap *bitmap )
751 for( y=0; y<bitmap->height; ++y )
753 uint8_t *dest = pic->Y_PIXELS+pic->Y_PITCH*y;
754 const uint8_t *src = bitmap->pixels+y*bitmap->width;
755 memcpy( dest, src, bitmap->width );
759 static void CreateKatePalette( video_palette_t *fmt_palette, const kate_palette *palette )
763 fmt_palette->i_entries = palette->ncolors;
764 for( n=0; n<palette->ncolors; ++n )
767 &fmt_palette->palette[n][0], &fmt_palette->palette[n][1], &fmt_palette->palette[n][2],
768 palette->colors[n].r, palette->colors[n].g, palette->colors[n].b
770 fmt_palette->palette[n][3] = palette->colors[n].a;
774 static void SetupText( decoder_t *p_dec, subpicture_t *p_spu, const kate_event *ev )
776 decoder_sys_t *p_sys = p_dec->p_sys;
778 if( ev->text_encoding != kate_utf8 )
780 msg_Warn( p_dec, "Text isn't UTF-8, unsupported, ignored" );
784 switch( ev->text_markup_type )
786 case kate_markup_none:
787 p_spu->p_region->psz_text = strdup( ev->text ); /* no leak, this actually gets killed by the core */
789 case kate_markup_simple:
790 if( p_sys->b_formatted )
792 /* the HTML renderer expects a top level text tag pair */
794 if( asprintf( &buffer, "<text>%s</text>", ev->text ) >= 0 )
796 p_spu->p_region->psz_html = buffer;
800 /* if not formatted, we fall through */
802 /* we don't know about this one, so remove markup and display as text */
804 char *copy = strdup( ev->text );
805 size_t len0 = strlen( copy ) + 1;
806 kate_text_remove_markup( ev->text_encoding, copy, &len0 );
807 p_spu->p_region->psz_text = copy;
815 static void TigerDestroySubpicture( subpicture_t *p_subpic )
817 DecSysRelease( p_subpic->p_sys->p_dec_sys );
820 static void SubpictureReleaseRegions( subpicture_t *p_subpic )
822 if( p_subpic->p_region)
824 subpicture_region_ChainDelete( p_subpic->p_region );
825 p_subpic->p_region = NULL;
829 static void TigerPreRender( spu_t *p_spu, subpicture_t *p_subpic, const video_format_t *p_fmt )
831 decoder_sys_t *p_sys = p_subpic->p_sys->p_dec_sys;
836 p_sys->p_spu_final = p_subpic;
840 * We get premultiplied alpha, but VLC doesn't expect this, so we demultiply
841 * alpha to avoid double multiply (and thus thinner text than we should)).
842 * Best would be to have VLC be able to handle premultiplied alpha, as it
843 * would be faster to blend as well.
845 * Also, we flip color components around for big endian machines, as Tiger
846 * outputs ARGB or ABGR (the one we selected here) in host endianness.
848 static void PostprocessTigerImage( plane_t *p_plane, unsigned int i_width )
850 PROFILE_START( tiger_renderer_postprocess );
852 for( y=0; y<p_plane->i_lines; ++y )
854 uint8_t *p_line = (uint8_t*)(p_plane->p_pixels + y*p_plane->i_pitch);
856 for( x=0; x<i_width; ++x )
858 uint8_t *p_pixel = p_line+x*4;
859 #ifdef WORDS_BIGENDIAN
860 uint8_t a = p_pixel[0];
862 uint8_t a = p_pixel[3];
866 #ifdef WORDS_BIGENDIAN
867 uint8_t tmp = pixel[2];
868 p_pixel[0] = p_pixel[3] * 255 / a;
870 p_pixel[2] = p_pixel[1] * 255 / a;
871 p_pixel[1] = tmp * 255 / a;
873 p_pixel[0] = p_pixel[0] * 255 / a;
874 p_pixel[1] = p_pixel[1] * 255 / a;
875 p_pixel[2] = p_pixel[2] * 255 / a;
887 PROFILE_STOP( tiger_renderer_postprocess );
890 /* Tiger renders can end up looking a bit crap since they get overlaid on top of
891 a subsampled YUV image, so there can be a fair amount of chroma bleeding.
892 Looks good with white though since it's all luma. Hopefully that will be the
894 static void TigerUpdateRegions( spu_t *p_spu, subpicture_t *p_subpic, const video_format_t *p_fmt, mtime_t ts )
896 decoder_sys_t *p_sys = p_subpic->p_sys->p_dec_sys;
897 subpicture_region_t *p_r;
905 PROFILE_START( TigerUpdateRegions );
907 /* do not render more than once per frame, libtiger renders all events at once */
908 if (ts <= p_sys->last_render_ts)
910 SubpictureReleaseRegions( p_subpic );
914 /* remember what frame we've rendered already */
915 p_sys->last_render_ts = ts;
917 if( p_subpic != p_sys->p_spu_final )
919 SubpictureReleaseRegions( p_subpic );
923 /* time in seconds from the start of the stream */
924 t = (p_subpic->p_sys->i_start + ts - p_subpic->i_start ) / 1000000.0f;
926 /* it is likely that the current region (if any) can be kept as is; test for this */
927 vlc_mutex_lock( &p_sys->lock );
928 if( p_subpic->p_region && !p_sys->b_dirty && !tiger_renderer_is_dirty( p_sys->p_tr ))
930 PROFILE_START( tiger_renderer_update1 );
931 i_ret = tiger_renderer_update( p_sys->p_tr, t, 1 );
932 PROFILE_STOP( tiger_renderer_update1 );
935 SubpictureReleaseRegions( p_subpic );
936 vlc_mutex_unlock( &p_sys->lock );
940 if( !tiger_renderer_is_dirty( p_sys->p_tr ) )
942 /* we can keep the current region list */
943 PROFILE_STOP( TigerUpdateRegions );
944 vlc_mutex_unlock( &p_sys->lock );
948 vlc_mutex_unlock( &p_sys->lock );
950 /* we have to render again, reset current region list */
951 SubpictureReleaseRegions( p_subpic );
953 /* create a full frame region - this will also tell Tiger the size of the frame */
955 fmt.i_chroma = VLC_CODEC_RGBA;
956 fmt.i_width = fmt.i_visible_width;
957 fmt.i_height = fmt.i_visible_height;
958 fmt.i_bits_per_pixel = 0;
959 fmt.i_x_offset = fmt.i_y_offset = 0;
961 p_r = subpicture_region_New( &fmt );
969 p_r->i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT;
971 vlc_mutex_lock( &p_sys->lock );
973 p_plane = &p_r->p_picture->p[0];
974 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 );
980 PROFILE_START( tiger_renderer_update );
981 i_ret = tiger_renderer_update( p_sys->p_tr, t, 1 );
986 PROFILE_STOP( tiger_renderer_update );
988 PROFILE_START( tiger_renderer_render );
989 i_ret = tiger_renderer_render( p_sys->p_tr );
994 PROFILE_STOP( tiger_renderer_render );
996 PostprocessTigerImage( p_plane, fmt.i_width );
997 p_subpic->p_region = p_r;
998 p_sys->b_dirty = false;
1000 PROFILE_STOP( TigerUpdateRegions );
1002 vlc_mutex_unlock( &p_sys->lock );
1007 vlc_mutex_unlock( &p_sys->lock );
1008 subpicture_region_ChainDelete( p_r );
1011 static uint32_t GetTigerColor( decoder_t *p_dec, const char *psz_prefix )
1014 uint32_t i_color = 0;
1016 if( asprintf( &psz_tmp, "%s-color", psz_prefix ) >= 0 )
1018 uint32_t i_rgb = var_CreateGetInteger( p_dec, psz_tmp );
1019 var_Destroy( p_dec, psz_tmp );
1024 if( asprintf( &psz_tmp, "%s-alpha", psz_prefix ) >= 0 )
1026 uint32_t i_alpha = var_CreateGetInteger( p_dec, psz_tmp );
1027 var_Destroy( p_dec, psz_tmp );
1029 i_color |= (i_alpha << 24);
1035 static char *GetTigerString( decoder_t *p_dec, const char *psz_name )
1037 char *psz_value = var_CreateGetString( p_dec, psz_name );
1040 psz_value = strdup( psz_value );
1042 var_Destroy( p_dec, psz_name );
1046 static int GetTigerInteger( decoder_t *p_dec, const char *psz_name )
1048 int i_value = var_CreateGetInteger( p_dec, psz_name );
1049 var_Destroy( p_dec, psz_name );
1053 static double GetTigerFloat( decoder_t *p_dec, const char *psz_name )
1055 double f_value = var_CreateGetFloat( p_dec, psz_name );
1056 var_Destroy( p_dec, psz_name );
1060 static void UpdateTigerQuality( decoder_t *p_dec )
1062 decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
1063 CHECK_TIGER_RET( tiger_renderer_set_quality( p_sys->p_tr, p_sys->f_tiger_quality ) );
1064 p_sys->b_dirty = true;
1067 static void UpdateTigerFontDesc( decoder_t *p_dec )
1069 decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
1070 CHECK_TIGER_RET( tiger_renderer_set_default_font_description( p_sys->p_tr, p_sys->psz_tiger_default_font_desc ) );
1071 p_sys->b_dirty = true;
1074 static void UpdateTigerFontColor( decoder_t *p_dec )
1076 decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
1077 double f_a = ((p_sys->i_tiger_default_font_color >> 24) & 0xff) / 255.0;
1078 double f_r = ((p_sys->i_tiger_default_font_color >> 16) & 0xff) / 255.0;
1079 double f_g = ((p_sys->i_tiger_default_font_color >> 8) & 0xff) / 255.0;
1080 double f_b = (p_sys->i_tiger_default_font_color & 0xff) / 255.0;
1081 CHECK_TIGER_RET( tiger_renderer_set_default_font_color( p_sys->p_tr, f_r, f_g, f_b, f_a ) );
1082 p_sys->b_dirty = true;
1085 static void UpdateTigerBackgroundColor( decoder_t *p_dec )
1087 decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
1088 double f_a = ((p_sys->i_tiger_default_background_color >> 24) & 0xff) / 255.0;
1089 double f_r = ((p_sys->i_tiger_default_background_color >> 16) & 0xff) / 255.0;
1090 double f_g = ((p_sys->i_tiger_default_background_color >> 8) & 0xff) / 255.0;
1091 double f_b = (p_sys->i_tiger_default_background_color & 0xff) / 255.0;
1092 CHECK_TIGER_RET( tiger_renderer_set_default_background_fill_color( p_sys->p_tr, f_r, f_g, f_b, f_a ) );
1093 p_sys->b_dirty = true;
1096 static void UpdateTigerFontEffect( decoder_t *p_dec )
1098 decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
1099 CHECK_TIGER_RET( tiger_renderer_set_default_font_effect( p_sys->p_tr,
1100 p_sys->e_tiger_default_font_effect,
1101 p_sys->f_tiger_default_font_effect_strength ) );
1102 p_sys->b_dirty = true;
1105 static int OnConfigurationChanged( decoder_t *p_dec, const char *psz_var,
1106 vlc_value_t oldval, vlc_value_t newval )
1108 decoder_sys_t *p_sys = (decoder_sys_t*)p_dec->p_sys;
1110 VLC_UNUSED( oldval );
1112 vlc_mutex_lock( &p_sys->lock );
1114 msg_Dbg( p_dec, "OnConfigurationChanged: %s", psz_var );
1116 if( !p_sys->b_use_tiger || !p_sys->p_tr )
1118 vlc_mutex_unlock( &p_sys->lock );
1122 #define TEST_TIGER_VAR( name ) \
1123 if( !strcmp( name, psz_var ) )
1125 TEST_TIGER_VAR( "kate-tiger-quality" )
1127 p_sys->f_tiger_quality = newval.f_float;
1128 UpdateTigerQuality( p_dec );
1131 TEST_TIGER_VAR( "kate-tiger-default-font-desc" )
1133 if( p_sys->psz_tiger_default_font_desc )
1135 free( p_sys->psz_tiger_default_font_desc );
1136 p_sys->psz_tiger_default_font_desc = NULL;
1138 if( newval.psz_string )
1140 p_sys->psz_tiger_default_font_desc = strdup( newval.psz_string );
1142 UpdateTigerFontDesc( p_dec );
1145 TEST_TIGER_VAR( "kate-tiger-default-font-color" )
1147 p_sys->i_tiger_default_font_color = (p_sys->i_tiger_default_font_color & 0xff00000000) | newval.i_int;
1148 UpdateTigerFontColor( p_dec );
1151 TEST_TIGER_VAR( "kate-tiger-default-font-alpha" )
1153 p_sys->i_tiger_default_font_color = (p_sys->i_tiger_default_font_color & 0x00ffffff) | (newval.i_int<<24);
1154 UpdateTigerFontColor( p_dec );
1157 TEST_TIGER_VAR( "kate-tiger-default-background-color" )
1159 p_sys->i_tiger_default_background_color = (p_sys->i_tiger_default_background_color & 0xff00000000) | newval.i_int;
1160 UpdateTigerBackgroundColor( p_dec );
1163 TEST_TIGER_VAR( "kate-tiger-default-background-alpha" )
1165 p_sys->i_tiger_default_background_color = (p_sys->i_tiger_default_background_color & 0x00ffffff) | (newval.i_int<<24);
1166 UpdateTigerBackgroundColor( p_dec );
1169 TEST_TIGER_VAR( "kate-tiger-default-font-effect" )
1171 p_sys->e_tiger_default_font_effect = (tiger_font_effect)newval.i_int;
1172 UpdateTigerFontEffect( p_dec );
1175 TEST_TIGER_VAR( "kate-tiger-default-font-effect-strength" )
1177 p_sys->f_tiger_default_font_effect_strength = newval.f_float;
1178 UpdateTigerFontEffect( p_dec );
1181 #undef TEST_TIGER_VAR
1183 vlc_mutex_unlock( &p_sys->lock );
1188 static int TigerConfigurationCallback( vlc_object_t *p_this, const char *psz_var,
1189 vlc_value_t oldval, vlc_value_t newval,
1194 VLC_UNUSED( p_this );
1195 VLC_UNUSED( oldval );
1196 VLC_UNUSED( newval );
1197 VLC_UNUSED( p_data );
1199 vlc_mutex_lock( &kate_decoder_list_mutex );
1201 /* Update all existing decoders from the global user prefs */
1202 for( i_idx = 0; i_idx < kate_decoder_list_size; i_idx++ )
1204 decoder_t *p_dec = kate_decoder_list[ i_idx ];
1205 OnConfigurationChanged( p_dec, psz_var, oldval, newval );
1208 vlc_mutex_unlock( &kate_decoder_list_mutex );
1215 /*****************************************************************************
1216 * DecodePacket: decodes a Kate packet.
1217 *****************************************************************************/
1218 static subpicture_t *DecodePacket( decoder_t *p_dec, kate_packet *p_kp, block_t *p_block )
1220 decoder_sys_t *p_sys = p_dec->p_sys;
1221 const kate_event *ev = NULL;
1222 subpicture_t *p_spu = NULL;
1225 if( !p_sys->b_ready )
1227 msg_Err( p_dec, "Cannot decode Kate packet, decoder not initialized" );
1231 i_ret = kate_decode_packetin( &p_sys->k, p_kp );
1234 msg_Err( p_dec, "Kate failed to decode packet: %d", i_ret );
1238 i_ret = kate_decode_eventout( &p_sys->k, &ev );
1241 msg_Err( p_dec, "Kate failed to retrieve event: %d", i_ret );
1246 /* no event to go with this packet, this is normal */
1250 /* we have an event */
1253 p_spu = decoder_NewSubpicture( p_dec );
1256 /* this will happen for lyrics as there is no vout - so no error */
1257 /* msg_Err( p_dec, "Failed to allocate spu buffer" ); */
1261 p_spu->i_start = p_block->i_pts;
1262 p_spu->i_stop = p_block->i_pts + INT64_C(1000000)*ev->duration*p_sys->ki.gps_denominator/p_sys->ki.gps_numerator;
1263 p_spu->b_ephemer = false;
1264 p_spu->b_absolute = false;
1267 if( p_sys->b_use_tiger)
1269 /* setup the structure to get our decoder struct back */
1270 p_spu->p_sys = malloc( sizeof( subpicture_sys_t ));
1273 decoder_DeleteSubpicture( p_dec, p_spu );
1276 p_spu->p_sys->p_dec_sys = p_sys;
1277 p_spu->p_sys->i_start = p_block->i_pts;
1278 DecSysHold( p_sys );
1280 p_spu->b_absolute = true;
1282 /* add the event to tiger */
1283 vlc_mutex_lock( &p_sys->lock );
1284 CHECK_TIGER_RET( tiger_renderer_add_event( p_sys->p_tr, ev->ki, ev ) );
1285 vlc_mutex_unlock( &p_sys->lock );
1287 /* hookup render/update routines */
1288 p_spu->pf_pre_render = TigerPreRender;
1289 p_spu->pf_update_regions = TigerUpdateRegions;
1290 p_spu->pf_destroy = TigerDestroySubpicture;
1295 p_spu = SetupSimpleKateSPU( p_dec, p_spu, ev );
1301 /*****************************************************************************
1302 * SetupSimpleKateSPU: creates text/bitmap regions where appropriate
1303 *****************************************************************************/
1304 static subpicture_t *SetupSimpleKateSPU( decoder_t *p_dec, subpicture_t *p_spu,
1305 const kate_event *ev )
1307 decoder_sys_t *p_sys = p_dec->p_sys;
1309 subpicture_region_t *p_bitmap_region = NULL;
1310 video_palette_t palette;
1312 bool b_tracker_valid = false;
1315 /* these may be 0 for "not specified" */
1316 p_spu->i_original_picture_width = p_sys->ki.original_canvas_width;
1317 p_spu->i_original_picture_height = p_sys->ki.original_canvas_height;
1319 /* Create a new subpicture region */
1320 memset( &fmt, 0, sizeof(video_format_t) );
1322 if (p_sys->b_formatted)
1324 i_ret = kate_tracker_init( &kin, &p_sys->ki, ev );
1327 msg_Err( p_dec, "failed to initialize kate tracker, event will be unformatted: %d", i_ret );
1331 int w = 720, h = 576; /* give sensible defaults just in case we fail to get the actual size */
1332 GetVideoSize(p_dec, &w, &h);
1333 i_ret = kate_tracker_update(&kin, 0, w, h, 0, 0, w, h);
1336 kate_tracker_clear(&kin);
1337 msg_Err( p_dec, "failed to update kate tracker, event will be unformatted: %d", i_ret );
1341 // TODO: parse tracker and set style, init fmt
1342 b_tracker_valid = true;
1347 if (ev->bitmap && ev->bitmap->type==kate_bitmap_type_paletted && ev->palette) {
1349 /* create a separate region for the bitmap */
1350 memset( &fmt, 0, sizeof(video_format_t) );
1351 fmt.i_chroma = VLC_CODEC_YUVP;
1353 fmt.i_width = fmt.i_visible_width = ev->bitmap->width;
1354 fmt.i_height = fmt.i_visible_height = ev->bitmap->height;
1355 fmt.i_x_offset = fmt.i_y_offset = 0;
1356 fmt.p_palette = &palette;
1357 CreateKatePalette( fmt.p_palette, ev->palette );
1359 p_bitmap_region = subpicture_region_New( &fmt );
1360 if( !p_bitmap_region )
1362 msg_Err( p_dec, "cannot allocate SPU region" );
1363 decoder_DeleteSubpicture( p_dec, p_spu );
1367 /* create the bitmap */
1368 CreateKateBitmap( p_bitmap_region->p_picture, ev->bitmap );
1370 msg_Dbg(p_dec, "Created bitmap, %zux%zu, %zu colors", ev->bitmap->width, ev->bitmap->height, ev->palette->ncolors);
1374 fmt.i_chroma = VLC_CODEC_TEXT;
1376 fmt.i_width = fmt.i_height = 0;
1377 fmt.i_x_offset = fmt.i_y_offset = 0;
1378 p_spu->p_region = subpicture_region_New( &fmt );
1379 if( !p_spu->p_region )
1381 msg_Err( p_dec, "cannot allocate SPU region" );
1382 decoder_DeleteSubpicture( p_dec, p_spu );
1386 SetupText( p_dec, p_spu, ev );
1388 /* default positioning */
1389 p_spu->p_region->i_align = SUBPICTURE_ALIGN_BOTTOM;
1390 if (p_bitmap_region)
1392 p_bitmap_region->i_align = SUBPICTURE_ALIGN_BOTTOM;
1394 p_spu->p_region->i_x = 0;
1395 p_spu->p_region->i_y = 10;
1397 /* override if tracker info present */
1398 if (b_tracker_valid)
1402 p_spu->p_region->i_x = kin.region_x;
1403 p_spu->p_region->i_y = kin.region_y;
1404 if (p_bitmap_region)
1406 p_bitmap_region->i_x = kin.region_x;
1407 p_bitmap_region->i_y = kin.region_y;
1409 p_spu->b_absolute = true;
1412 kate_tracker_clear(&kin);
1415 /* if we have a bitmap, chain it before the text */
1416 if (p_bitmap_region)
1418 p_bitmap_region->p_next = p_spu->p_region;
1419 p_spu->p_region = p_bitmap_region;
1425 /*****************************************************************************
1426 * ParseKateComments:
1427 *****************************************************************************/
1428 static void ParseKateComments( decoder_t *p_dec )
1430 char *psz_name, *psz_value, *psz_comment;
1433 while ( i < p_dec->p_sys->kc.comments )
1435 psz_comment = strdup( p_dec->p_sys->kc.user_comments[i] );
1438 psz_name = psz_comment;
1439 psz_value = strchr( psz_comment, '=' );
1445 if( !p_dec->p_description )
1446 p_dec->p_description = vlc_meta_New();
1447 if( p_dec->p_description )
1448 vlc_meta_AddExtra( p_dec->p_description, psz_name, psz_value );
1450 free( psz_comment );
1455 /*****************************************************************************
1456 * CloseDecoder: clean up the decoder
1457 *****************************************************************************/
1458 static void CloseDecoder( vlc_object_t *p_this )
1460 decoder_t *p_dec = (decoder_t *)p_this;
1463 /* remove the decoder from the global list */
1464 vlc_mutex_lock( &kate_decoder_list_mutex );
1465 for( i_index = 0; i_index < kate_decoder_list_size; i_index++ )
1467 if( kate_decoder_list[ i_index ] == p_dec )
1469 kate_decoder_list[ i_index ] = kate_decoder_list[ --kate_decoder_list_size ];
1473 vlc_mutex_unlock( &kate_decoder_list_mutex );
1475 msg_Dbg( p_dec, "Closing Kate decoder" );
1476 DecSysRelease( p_dec->p_sys );
1479 static void DecSysHold( decoder_sys_t *p_sys )
1481 vlc_mutex_lock( &p_sys->lock );
1482 p_sys->i_refcount++;
1483 vlc_mutex_unlock( &p_sys->lock );
1486 static void DecSysRelease( decoder_sys_t *p_sys )
1488 vlc_mutex_lock( &p_sys->lock );
1489 p_sys->i_refcount--;
1490 if( p_sys->i_refcount > 0)
1492 vlc_mutex_unlock( &p_sys->lock );
1496 vlc_mutex_unlock( &p_sys->lock );
1497 vlc_mutex_destroy( &p_sys->lock );
1501 tiger_renderer_destroy( p_sys->p_tr );
1502 if( p_sys->psz_tiger_default_font_desc )
1503 free( p_sys->psz_tiger_default_font_desc );
1507 kate_clear( &p_sys->k );
1508 kate_info_clear( &p_sys->ki );
1509 kate_comment_clear( &p_sys->kc );