From: Vincent Penquerc'h Date: Fri, 2 May 2008 09:46:26 +0000 (+0100) Subject: basic support for Kate streams as subtitles X-Git-Tag: 0.9.0-test0~1243 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=53772811d1d5f1b2a22f36332a500ffac5eba29e;p=vlc basic support for Kate streams as subtitles Signed-off-by: RĂ©mi Denis-Courmont --- diff --git a/configure.ac b/configure.ac index 550ab0d024..f0cef7897e 100644 --- a/configure.ac +++ b/configure.ac @@ -4004,6 +4004,39 @@ then VLC_ADD_PLUGIN([cmml]) fi +dnl +dnl kate decoder plugin +dnl +AC_ARG_ENABLE(kate, +[ --enable-kate kate codec (default enabled)]) +if test "${enable_kate}" != "no"; then + PKG_CHECK_MODULES(KATE,[kate >= 0.1.1], [ + VLC_ADD_PLUGINS([kate]) + VLC_ADD_CFLAGS([kate],[$KATE_CFLAGS]) + VLC_ADD_LIBS([kate],[$KATE_LIBS]) ],[ + AC_CHECK_HEADERS(kate/kate.h, [ + AC_CHECK_LIB(kate, kate_decode_init, [ + if test "${SYS}" = "mingw32"; then + VLC_ADD_PLUGINS([kate]) + else + VLC_ADD_BUILTINS([kate]) + fi + kate_libs="-lkate -logg" + VLC_ADD_LDFLAGS([kate],[${kate_libs}]) ],[ + if test "${enable_kate}" == "yes"; then + AC_MSG_ERROR([libkate doesn't appear to be installed on your system. + You also need to check that you have a libogg posterior to the 1.0 release.]) + fi], + [-lkate -logg]) + ],[ + if test "${enable_kate}" == "yes"; then + AC_MSG_ERROR([libkate headers do not appear to be installed on your system. + You also need to check that you have a libogg posterior to the 1.0 release.]) + fi + ]) + ]) +fi + dnl dnl Video plugins diff --git a/modules/LIST b/modules/LIST index 18b9148fd4..6381840d01 100644 --- a/modules/LIST +++ b/modules/LIST @@ -160,6 +160,7 @@ $Id$ * inhibit: preventing the computer from suspending * invert: inverse video filter. * jack: jack server audio output + * kate: kate text bitstream decoder * libmpeg2: Mpeg2 video decoder using libmpeg2 * linear_resampler: linear audio resampler * lirc: Linux infrared control module diff --git a/modules/codec/Modules.am b/modules/codec/Modules.am index 0ff33a05dd..a5d977a431 100644 --- a/modules/codec/Modules.am +++ b/modules/codec/Modules.am @@ -33,3 +33,4 @@ SOURCES_csri = csri.c SOURCES_cdg = cdg.c SOURCES_fluidsynth = fluidsynth.c SOURCES_cc = cc.c cc.h +SOURCES_kate = kate.c diff --git a/modules/codec/kate.c b/modules/codec/kate.c new file mode 100644 index 0000000000..501ff13361 --- /dev/null +++ b/modules/codec/kate.c @@ -0,0 +1,656 @@ +/***************************************************************************** + * kate.c : a decoder for the kate bitstream format + ***************************************************************************** + * Copyright (C) 2000-2006 the VideoLAN team + * $Id$ + * + * Authors: Vincent Penquerc'h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include + +#include "vlc_osd.h" + +/* #define ENABLE_PACKETIZER */ +/* #define ENABLE_FORMATTING */ +/* #define ENABLE_BITMAPS */ + +/***************************************************************************** + * decoder_sys_t : decoder descriptor + *****************************************************************************/ +struct decoder_sys_t +{ +#ifdef ENABLE_PACKETIZER + /* Module mode */ + bool b_packetizer; +#endif + + /* + * Input properties + */ + int i_num_headers; + int i_headers; + + /* + * Kate properties + */ + bool b_ready; + kate_info ki; + kate_comment kc; + kate_state k; + + /* + * Common properties + */ + mtime_t i_pts; + + /* + * Options + */ +#ifdef ENABLE_FORMATTING + bool b_formatted; +#endif +}; + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int OpenDecoder ( vlc_object_t * ); +static void CloseDecoder ( vlc_object_t * ); +#ifdef ENABLE_PACKETIZER +static int OpenPacketizer( vlc_object_t *p_this ); +#endif + +static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ); +static int ProcessHeaders( decoder_t *p_dec ); +static subpicture_t *ProcessPacket( decoder_t *p_dec, kate_packet *p_kp, + block_t **pp_block ); +static subpicture_t *DecodePacket( decoder_t *p_dec, kate_packet *p_kp, + block_t *p_block ); +static void ParseKateComments( decoder_t * ); + +#define DEFAULT_NAME "Default" +#define MAX_LINE 8192 + +/***************************************************************************** + * 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.") +#endif + + +vlc_module_begin(); + set_shortname( _("Kate")); + set_description( _("Kate text subtitles decoder") ); + set_capability( "decoder", 50 ); + set_callbacks( OpenDecoder, CloseDecoder ); + set_category( CAT_INPUT ); + set_subcategory( SUBCAT_INPUT_SCODEC ); + add_shortcut( "kate" ); + +#ifdef ENABLE_PACKETIZER + add_submodule(); + set_description( _("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, + false ); +#endif +vlc_module_end(); + +/***************************************************************************** + * OpenDecoder: probe the decoder and return score + ***************************************************************************** + * Tries to launch a decoder and return score so that the interface is able + * to chose. + *****************************************************************************/ +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"); + + if( p_dec->fmt_in.i_codec != VLC_FOURCC('k','a','t','e') ) + { + return VLC_EGENERIC; + } + + /* Set callbacks */ + p_dec->pf_decode_sub = (subpicture_t *(*)(decoder_t *, block_t **)) + DecodeBlock; + p_dec->pf_packetize = (block_t *(*)(decoder_t *, block_t **)) + DecodeBlock; + + /* Allocate the memory needed to store the decoder's structure */ + if( ( p_dec->p_sys = p_sys = + (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL ) + { + msg_Err( p_dec, "out of memory" ); + return VLC_ENOMEM; + } + + /* init of p_sys */ +#ifdef ENABLE_PACKETIZER + p_sys->b_packetizer = false; +#endif + p_sys->b_ready = false; + p_sys->i_pts = 0; + + kate_comment_init( &p_sys->kc ); + kate_info_init( &p_sys->ki ); + + p_sys->i_num_headers = 0; + p_sys->i_headers = 0; + + /* retrieve options */ +#ifdef ENABLE_FORMATTING + p_sys->b_formatted = var_CreateGetBool( p_dec, "kate-formatted" ); +#endif + + return VLC_SUCCESS; +} + +#ifdef ENABLE_PACKETIZER +static int OpenPacketizer( vlc_object_t *p_this ) +{ + decoder_t *p_dec = (decoder_t*)p_this; + + int i_ret = OpenDecoder( p_this ); + + if( i_ret == VLC_SUCCESS ) + { + p_dec->p_sys->b_packetizer = true; + p_dec->fmt_out.i_codec = VLC_FOURCC( 'k', 'a', 't', 'e' ); + } + + return i_ret; +} +#endif + +/**************************************************************************** + * DecodeBlock: the whole thing + **************************************************************************** + * This function must be fed with kate packets. + ****************************************************************************/ +static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) +{ + decoder_sys_t *p_sys = p_dec->p_sys; + block_t *p_block; + kate_packet kp; + + if( !pp_block || !*pp_block ) return NULL; + + p_block = *pp_block; + + /* Block to Kate packet */ + kate_packet_wrap(&kp, p_block->i_buffer, p_block->p_buffer); + + if( p_sys->i_headers == 0 && p_dec->fmt_in.i_extra ) + { + /* Headers already available as extra data */ + p_sys->i_num_headers = ((unsigned char*)p_dec->fmt_in.p_extra)[0]; + p_sys->i_headers = p_sys->i_num_headers; + } + else if( kp.nbytes && (p_sys->i_headers==0 || p_sys->i_headers < p_sys->ki.num_headers )) + { + /* Backup headers as extra data */ + uint8_t *p_extra; + + p_dec->fmt_in.p_extra = + realloc( p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra + kp.nbytes + 2 ); + p_extra = (void*)(((unsigned char*)p_dec->fmt_in.p_extra) + p_dec->fmt_in.i_extra); + *(p_extra++) = kp.nbytes >> 8; + *(p_extra++) = kp.nbytes & 0xFF; + + memcpy( p_extra, kp.data, kp.nbytes ); + p_dec->fmt_in.i_extra += kp.nbytes + 2; + + block_Release( *pp_block ); + p_sys->i_num_headers = ((unsigned char*)p_dec->fmt_in.p_extra)[0]; + p_sys->i_headers++; + return NULL; + } + + if( p_sys->i_headers == p_sys->i_num_headers && p_sys->i_num_headers>0 ) + { + if( ProcessHeaders( p_dec ) != VLC_SUCCESS ) + { + p_sys->i_headers = 0; + p_dec->fmt_in.i_extra = 0; + block_Release( *pp_block ); + return NULL; + } + else p_sys->i_headers++; + } + + return ProcessPacket( p_dec, &kp, pp_block ); +} + +/***************************************************************************** + * ProcessHeaders: process Kate headers. + *****************************************************************************/ +static int ProcessHeaders( decoder_t *p_dec ) +{ + decoder_sys_t *p_sys = p_dec->p_sys; + kate_packet kp; + uint8_t *p_extra; + int i_extra; + int headeridx; + int ret; + + if( !p_dec->fmt_in.i_extra ) return VLC_EGENERIC; + + p_extra = p_dec->fmt_in.p_extra; + i_extra = p_dec->fmt_in.i_extra; + + /* skip number of headers */ + ++p_extra; + --i_extra; + + /* Take care of the initial Kate header */ + kp.nbytes = *(p_extra++) << 8; + kp.nbytes |= (*(p_extra++) & 0xFF); + kp.data = p_extra; + p_extra += kp.nbytes; + i_extra -= (kp.nbytes + 2); + if( i_extra < 0 ) + { + msg_Err( p_dec, "header data corrupted"); + return VLC_EGENERIC; + } + + ret = kate_decode_headerin( &p_sys->ki, &p_sys->kc, &kp ); + if( ret < 0 ) + { + msg_Err( p_dec, "this bitstream does not contain Kate data (%d)", ret ); + return VLC_EGENERIC; + } + + msg_Dbg( p_dec, "%s %s text, granule rate %f, granule shift %d", + p_sys->ki.language, p_sys->ki.category, + (double)p_sys->ki.gps_numerator/p_sys->ki.gps_denominator, + p_sys->ki.granule_shift); + + /* we want markup to be removed for now */ + kate_info_remove_markup( &p_sys->ki, 1 ); + + /* parse all remaining header packets */ + for (headeridx=1; headeridxki.num_headers; ++headeridx) + { + kp.nbytes = *(p_extra++) << 8; + kp.nbytes |= (*(p_extra++) & 0xFF); + kp.data = p_extra; + p_extra += kp.nbytes; + i_extra -= (kp.nbytes + 2); + if( i_extra < 0 ) + { + msg_Err( p_dec, "header %d data corrupted", headeridx); + return VLC_EGENERIC; + } + + ret = kate_decode_headerin( &p_sys->ki, &p_sys->kc, &kp ); + if( ret < 0 ) + { + msg_Err( p_dec, "Kate header %d is corrupted: %d", headeridx, ret); + return VLC_EGENERIC; + } + + /* header 1 is comments */ + if( headeridx == 1 ) + { + ParseKateComments( p_dec ); + } + } + +#ifdef ENABLE_PACKETIZER + if( !p_sys->b_packetizer ) +#endif + { + /* 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) + { + msg_Err( p_dec, "Kate failed to initialize for decoding: %d", ret ); + return VLC_EGENERIC; + } + p_sys->b_ready = true; + } +#ifdef ENABLE_PACKETIZER + else + { + p_dec->fmt_out.i_extra = p_dec->fmt_in.i_extra; + p_dec->fmt_out.p_extra = + realloc( p_dec->fmt_out.p_extra, p_dec->fmt_out.i_extra ); + memcpy( p_dec->fmt_out.p_extra, + p_dec->fmt_in.p_extra, p_dec->fmt_out.i_extra ); + } +#endif + + return VLC_SUCCESS; +} + +/***************************************************************************** + * ProcessPacket: processes a kate packet. + *****************************************************************************/ +static subpicture_t *ProcessPacket( decoder_t *p_dec, kate_packet *p_kp, + block_t **pp_block ) +{ + decoder_sys_t *p_sys = p_dec->p_sys; + block_t *p_block = *pp_block; + subpicture_t *p_buf = NULL; + + /* Date management */ + if( p_block->i_pts > 0 && p_block->i_pts != p_sys->i_pts ) + { + p_sys->i_pts = p_block->i_pts; + } + + *pp_block = NULL; /* To avoid being fed the same packet again */ + +#ifdef ENABLE_PACKETIZER + if( p_sys->b_packetizer ) + { + /* Date management */ + p_block->i_dts = p_block->i_pts = p_sys->i_pts; + + if( p_sys->i_headers >= p_sys->i_num_headers ) + p_block->i_length = p_sys->i_pts - p_block->i_pts; + else + p_block->i_length = 0; + + p_buf = p_block; + } + else +#endif + { + if( p_sys->i_headers >= p_sys->i_num_headers ) + p_buf = DecodePacket( p_dec, p_kp, p_block ); + else + p_buf = NULL; + + if( p_block ) block_Release( p_block ); + } + + 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 ) +{ + *y = ( ( ( 66 * r + 129 * g + 25 * b + 128 ) >> 8 ) + 16 ); + *u = ( ( -38 * r - 74 * g + 112 * b + 128 ) >> 8 ) + 128 ; + *v = ( ( 112 * r - 94 * g - 18 * b + 128 ) >> 8 ) + 128 ; +} +#endif + +/***************************************************************************** + * DecodePacket: decodes a Kate packet. + *****************************************************************************/ +static subpicture_t *DecodePacket( decoder_t *p_dec, kate_packet *p_kp, block_t *p_block ) +{ + 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; +#ifdef ENABLE_BITMAPS + size_t n, y; + picture_t *pic = NULL; +#endif + video_format_t fmt; + kate_tracker kin; + bool tracker_valid = false; + + ret = kate_decode_packetin( &p_sys->k, p_kp ); + if( ret < 0 ) + { + msg_Err( p_dec, "Kate failed to decode packet: %d", ret ); + return NULL; + } + + ret = kate_decode_eventout( &p_sys->k, &ev ); + if( ret < 0 ) + { + msg_Err( p_dec, "Kate failed to retrieve event: %d", ret ); + return NULL; + } + if( ret > 0 ) + { + /* no event to go with this packet, this is normal */ + return NULL; + } + + /* we have an event */ + + /* Get a new spu */ + p_spu = p_dec->pf_spu_buffer_new( p_dec ); + if( !p_spu ) + { + msg_Err( p_dec, "Failed to allocate spu buffer" ); + return NULL; + } + + p_spu->b_pausable = true; + +#ifdef ENABLE_BITMAPS + if (ev->bitmap && 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'); + fmt.i_aspect = 0; + 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; + + p_bitmap_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt ); + if( !p_bitmap_region ) + { + msg_Err( p_dec, "cannot allocate SPU region" ); + p_dec->pf_spu_buffer_del( p_dec, p_spu ); + return NULL; + } + + /* create the palette */ + fmt.p_palette->i_entries = ev->palette->ncolors; + for (n=0; npalette->ncolors; ++n) + { + rgb_to_yuv( + &fmt.p_palette->palette[n][0], &fmt.p_palette->palette[n][1], &fmt.p_palette->palette[n][2], + ev->palette->colors[n].r, ev->palette->colors[n].g, ev->palette->colors[n].b + ); + fmt.p_palette->palette[n][3] = ev->palette->colors[n].a; + } + + /* create the bitmap */ + pic = &p_bitmap_region->picture; + for (y=0; ybitmap->height; ++y) { + uint8_t *dest=pic->Y_PIXELS+pic->Y_PITCH*y; + const uint8_t *src=ev->bitmap->pixels+y*ev->bitmap->width; + memcpy(dest, src, ev->bitmap->width); + } + + msg_Dbg(p_dec, "Created bitmap, %dx%d, %d colors\n", ev->bitmap->width, ev->bitmap->height, ev->palette->ncolors); + } +#endif + + /* Create a new subpicture region */ + memset( &fmt, 0, sizeof(video_format_t) ); + 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; + +#ifdef ENABLE_FORMATTING + if (p_sys->b_formatted) + { + ret = kate_tracker_init( &kin, &p_sys->ki, ev); + if( ret < 0) + { + msg_Err( p_dec, "failed to initialize kate tracker, event will be unformatted: %d", ret ); + } + else + { + // TODO: get window/video sizes/pos - can't find where to get those ! + int w = 640; + int h = 480; + ret = kate_tracker_update(&kin, 0, w, h, 0, 0, w, h); + if( ret < 0) + { + kate_tracker_clear(&kin); + msg_Err( p_dec, "failed to update kate tracker, event will be unformatted: %d", ret ); + } + else + { + if (kin.has.region) + { + fmt.i_width = kin.region_w; + fmt.i_height = kin.region_h; + } + + // TODO: parse tracker and set style, init fmt + tracker_valid = true; + } + } + } +#endif + + + + p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt ); + if( !p_spu->p_region ) + { + msg_Err( p_dec, "cannot allocate SPU region" ); + p_dec->pf_spu_buffer_del( p_dec, p_spu ); + return NULL; + } + + p_spu->p_region->psz_text = strdup(ev->text); /* no leak, this actually gets killed by the core */ + p_spu->i_start = p_block->i_pts; + p_spu->i_stop = p_block->i_pts + I64C(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; + + if (tracker_valid) + { + p_spu->i_flags = 0; + 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; + } + } + + kate_tracker_clear(&kin); + } + else + { + /* Normal text subs, easy markup */ + 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; + } + +#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 + *****************************************************************************/ +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 ); + } + free( psz_comment ); + i++; + } +} + +/***************************************************************************** + * CloseDecoder: clean up the decoder + *****************************************************************************/ +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; + + if (p_sys->b_ready) + kate_clear( &p_sys->k ); + kate_info_clear( &p_sys->ki ); + kate_comment_clear( &p_sys->kc ); + + free( p_sys ); +} + diff --git a/modules/demux/mkv.cpp b/modules/demux/mkv.cpp index e14a94750e..52a338407a 100644 --- a/modules/demux/mkv.cpp +++ b/modules/demux/mkv.cpp @@ -2550,6 +2550,58 @@ bool matroska_segment_c::Select( mtime_t i_start_time ) } tracks[i_track]->fmt.audio.i_blockalign = ( tracks[i_track]->fmt.audio.i_bitspersample + 7 ) / 8 * tracks[i_track]->fmt.audio.i_channels; } +#if 0 + /* disabled due to the potential "S_KATE" namespace issue */ + else if( !strcmp( tracks[i_track]->psz_codec, "S_KATE" ) ) + { + int i, i_offset = 1, *i_size, i_extra, num_headers, size_so_far; + uint8_t *p_extra; + + tracks[i_track]->fmt.i_codec = VLC_FOURCC( 'k', 'a', 't', 'e' ); + tracks[i_track]->fmt.subs.psz_encoding = strdup( "UTF-8" ); + + /* Recover the number of headers to expect */ + num_headers = tracks[i_track]->p_extra_data[0]+1; + msg_Dbg( &sys.demuxer, "kate in mkv detected: %d headers in %u bytes", + num_headers, tracks[i_track]->i_extra_data); + + /* this won't overflow the stack as is can allocate only 1020 bytes max */ + i_size = (int*)alloca(num_headers*sizeof(int)); + + /* Split the headers */ + size_so_far = 0; + for( i = 0; i < num_headers-1; i++ ) + { + i_size[i] = 0; + while( i_offset < tracks[i_track]->i_extra_data ) + { + i_size[i] += tracks[i_track]->p_extra_data[i_offset]; + if( tracks[i_track]->p_extra_data[i_offset++] != 0xff ) break; + } + msg_Dbg( &sys.demuxer, "kate header %d is %d bytes", i, i_size[i]); + size_so_far += i_size[i]; + } + i_size[num_headers-1] = tracks[i_track]->i_extra_data - (size_so_far+i_offset); + msg_Dbg( &sys.demuxer, "kate last header (%d) is %d bytes", num_headers-1, i_size[num_headers-1]); + + tracks[i_track]->fmt.i_extra = 1 + num_headers * 2 + size_so_far + i_size[num_headers-1]; + tracks[i_track]->fmt.p_extra = malloc( tracks[i_track]->fmt.i_extra ); + + p_extra = (uint8_t *)tracks[i_track]->fmt.p_extra; + i_extra = 0; + *(p_extra++) = num_headers; + ++i_extra; + for( i = 0; i < num_headers; i++ ) + { + *(p_extra++) = i_size[i] >> 8; + *(p_extra++) = i_size[i] & 0xFF; + memcpy( p_extra, tracks[i_track]->p_extra_data + i_offset + i_extra-1, + i_size[i] ); + p_extra += i_size[i]; + i_extra += i_size[i]; + } + } +#endif else if( !strcmp( tracks[i_track]->psz_codec, "S_TEXT/UTF8" ) ) { tracks[i_track]->fmt.i_codec = VLC_FOURCC( 's', 'u', 'b', 't' ); diff --git a/modules/demux/ogg.c b/modules/demux/ogg.c index 6919a8dae9..9dcfdac7f0 100644 --- a/modules/demux/ogg.c +++ b/modules/demux/ogg.c @@ -85,7 +85,10 @@ typedef struct logical_stream_s /* Misc */ int b_reinit; - int i_theora_keyframe_granule_shift; + int i_granule_shift; + + /* kate streams have the number of headers in the ID header */ + int i_kate_num_headers; /* for Annodex logical bitstreams */ int secondary_header_packets; @@ -175,6 +178,7 @@ static void Ogg_EndOfStream( demux_t *p_demux ); static void Ogg_ReadTheoraHeader( logical_stream_t *, ogg_packet * ); static void Ogg_ReadVorbisHeader( logical_stream_t *, ogg_packet * ); static void Ogg_ReadSpeexHeader( logical_stream_t *, ogg_packet * ); +static void Ogg_ReadKateHeader( logical_stream_t *, ogg_packet * ); static void Ogg_ReadFlacHeader( demux_t *, logical_stream_t *, ogg_packet * ); static void Ogg_ReadAnnodexHeader( vlc_object_t *, logical_stream_t *, ogg_packet * ); @@ -443,21 +447,22 @@ static void Ogg_UpdatePCR( logical_stream_t *p_stream, /* Convert the granulepos into a pcr */ if( p_oggpacket->granulepos >= 0 ) { - if( p_stream->fmt.i_codec != VLC_FOURCC( 't','h','e','o' ) ) - { - p_stream->i_pcr = p_oggpacket->granulepos * INT64_C(1000000) - / p_stream->f_rate; - } - else + if( p_stream->fmt.i_codec == VLC_FOURCC( 't','h','e','o' ) || + p_stream->fmt.i_codec == VLC_FOURCC( 'k','a','t','e' ) ) { ogg_int64_t iframe = p_oggpacket->granulepos >> - p_stream->i_theora_keyframe_granule_shift; + p_stream->i_granule_shift; ogg_int64_t pframe = p_oggpacket->granulepos - - ( iframe << p_stream->i_theora_keyframe_granule_shift ); + ( iframe << p_stream->i_granule_shift ); p_stream->i_pcr = ( iframe + pframe ) * INT64_C(1000000) / p_stream->f_rate; } + else + { + p_stream->i_pcr = p_oggpacket->granulepos * INT64_C(1000000) + / p_stream->f_rate; + } p_stream->i_interpolated_pcr = p_stream->i_pcr; } @@ -520,6 +525,7 @@ static void Ogg_DecodePacket( demux_t *p_demux, { uint8_t *p_extra; bool b_store_size = true; + bool b_store_num_headers = false; p_stream->i_packets_backup++; switch( p_stream->fmt.i_codec ) @@ -548,6 +554,12 @@ static void Ogg_DecodePacket( demux_t *p_demux, b_store_size = false; break; + case VLC_FOURCC( 'k','a','t','e' ): + if( p_stream->i_packets_backup == 1) + b_store_num_headers = true; + if( p_stream->i_packets_backup == p_stream->i_kate_num_headers ) p_stream->b_force_backup = 0; + break; + default: p_stream->b_force_backup = 0; break; @@ -556,15 +568,21 @@ static void Ogg_DecodePacket( demux_t *p_demux, /* Backup the ogg packet (likely an header packet) */ p_stream->p_headers = realloc( p_stream->p_headers, p_stream->i_headers + - p_oggpacket->bytes + (b_store_size ? 2 : 0) ); + p_oggpacket->bytes + (b_store_size ? 2 : 0) + (b_store_num_headers ? 1 : 0) ); p_extra = p_stream->p_headers + p_stream->i_headers; + if( b_store_num_headers ) + { + /* Kate streams store the number of headers in the first header, + so we can't just test for 3 as Vorbis/Theora */ + *(p_extra++) = p_stream->i_kate_num_headers; + } if( b_store_size ) { *(p_extra++) = p_oggpacket->bytes >> 8; *(p_extra++) = p_oggpacket->bytes & 0xFF; } memcpy( p_extra, p_oggpacket->packet, p_oggpacket->bytes ); - p_stream->i_headers += p_oggpacket->bytes + (b_store_size ? 2 : 0); + p_stream->i_headers += p_oggpacket->bytes + (b_store_size ? 2 : 0) + (b_store_num_headers ? 1 : 0); if( !p_stream->b_force_backup ) { @@ -673,7 +691,8 @@ static void Ogg_DecodePacket( demux_t *p_demux, p_stream->fmt.i_codec != VLC_FOURCC( 'f','l','a','c' ) && p_stream->fmt.i_codec != VLC_FOURCC( 't','a','r','k' ) && p_stream->fmt.i_codec != VLC_FOURCC( 't','h','e','o' ) && - p_stream->fmt.i_codec != VLC_FOURCC( 'c','m','m','l' ) ) + p_stream->fmt.i_codec != VLC_FOURCC( 'c','m','m','l' ) && + p_stream->fmt.i_codec != VLC_FOURCC( 'k','a','t','e' ) ) { /* We remove the header from the packet */ i_header_len = (*p_oggpacket->packet & PACKET_LEN_BITS01) >> 6; @@ -877,6 +896,13 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) Ogg_ReadAnnodexHeader( VLC_OBJECT(p_demux), p_stream, &oggpacket ); } + /* Check for Kate header */ + else if( oggpacket.bytes >= 9 && + ! memcmp( &oggpacket.packet[1], "kate\0\0\0\0", 8 ) ) + { + Ogg_ReadKateHeader( p_stream, &oggpacket ); + msg_Dbg( p_demux, "found kate header" ); + } else if( oggpacket.bytes >= 142 && !memcmp( &oggpacket.packet[1], "Direct Show Samples embedded in Ogg", 35 )) @@ -1157,6 +1183,7 @@ static int Ogg_BeginningOfStream( demux_t *p_demux ) #define p_stream p_ogg->pp_stream[i_stream] p_stream->p_es = es_out_Add( p_demux->out, &p_stream->fmt ); + // TODO: something to do here ? if( p_stream->fmt.i_codec == VLC_FOURCC('c','m','m','l') ) { /* Set the CMML stream active */ @@ -1249,11 +1276,11 @@ static void Ogg_ReadTheoraHeader( logical_stream_t *p_stream, i_keyframe_frequency_force = 1 << bs_read( &bitstream, 5 ); /* granule_shift = i_log( frequency_force -1 ) */ - p_stream->i_theora_keyframe_granule_shift = 0; + p_stream->i_granule_shift = 0; i_keyframe_frequency_force--; while( i_keyframe_frequency_force ) { - p_stream->i_theora_keyframe_granule_shift++; + p_stream->i_granule_shift++; i_keyframe_frequency_force >>= 1; } @@ -1340,6 +1367,59 @@ static void Ogg_ReadFlacHeader( demux_t *p_demux, logical_stream_t *p_stream, } } +static void Ogg_ReadKateHeader( logical_stream_t *p_stream, + ogg_packet *p_oggpacket ) +{ + oggpack_buffer opb; + int32_t gnum; + int32_t gden; + int n; + + p_stream->fmt.i_cat = SPU_ES; + p_stream->fmt.i_codec = VLC_FOURCC( 'k','a','t','e' ); + + /* Signal that we want to keep a backup of the kate + * stream headers. They will be used when switching between + * kate streams. */ + p_stream->b_force_backup = 1; + + /* Cheat and get additionnal info ;) */ + oggpack_readinit( &opb, p_oggpacket->packet, p_oggpacket->bytes); + oggpack_adv( &opb, 11*8 ); /* packet type, kate magic, version */ + p_stream->i_kate_num_headers = oggpack_read( &opb, 8 ); + oggpack_adv( &opb, 3*8 ); + p_stream->i_granule_shift = oggpack_read( &opb, 8 ); + oggpack_adv( &opb, 8*8 ); /* reserved */ + gnum = oggpack_read( &opb, 32 ); + gden = oggpack_read( &opb, 32 ); + p_stream->f_rate = (double)gnum/gden; + + p_stream->fmt.psz_language = malloc(16); + if (p_stream->fmt.psz_language) + { + for (n=0;n<16;++n) + p_stream->fmt.psz_language[n] = oggpack_read(&opb,8); + p_stream->fmt.psz_language[15] = 0; /* just in case */ + } + else + { + for (n=0;n<16;++n) + oggpack_read(&opb,8); + } + p_stream->fmt.psz_description = malloc(16); + if (p_stream->fmt.psz_description) + { + for (n=0;n<16;++n) + p_stream->fmt.psz_description[n] = oggpack_read(&opb,8); + p_stream->fmt.psz_description[15] = 0; /* just in case */ + } + else + { + for (n=0;n<16;++n) + oggpack_read(&opb,8); + } +} + static void Ogg_ReadAnnodexHeader( vlc_object_t *p_this, logical_stream_t *p_stream, ogg_packet *p_oggpacket )