basic support for Kate streams as subtitles
authorVincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
Fri, 2 May 2008 09:46:26 +0000 (10:46 +0100)
committerRémi Denis-Courmont <rem@videolan.org>
Fri, 2 May 2008 15:25:05 +0000 (18:25 +0300)
Signed-off-by: Rémi Denis-Courmont <rem@videolan.org>
configure.ac
modules/LIST
modules/codec/Modules.am
modules/codec/kate.c [new file with mode: 0644]
modules/demux/mkv.cpp
modules/demux/ogg.c

index 550ab0d..f0cef78 100644 (file)
@@ -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
index 18b9148..6381840 100644 (file)
@@ -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
index 0ff33a0..a5d977a 100644 (file)
@@ -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 (file)
index 0000000..501ff13
--- /dev/null
@@ -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 <ogg.k.ogg.k@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_input.h>
+#include <vlc_codec.h>
+
+#include <kate/kate.h>
+
+#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; headeridx<p_sys->ki.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; n<ev->palette->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; y<ev->bitmap->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 );
+}
+
index e14a947..52a3384 100644 (file)
@@ -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' );
index 6919a8d..9dcfdac 100644 (file)
@@ -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 )