]> git.sesse.net Git - vlc/commitdiff
oups, ... forgot the interesting file !
authorDamien Lucas <nitrox@videolan.org>
Thu, 6 Nov 2003 16:37:19 +0000 (16:37 +0000)
committerDamien Lucas <nitrox@videolan.org>
Thu, 6 Nov 2003 16:37:19 +0000 (16:37 +0000)
modules/codec/dvbsub.c [new file with mode: 0644]

diff --git a/modules/codec/dvbsub.c b/modules/codec/dvbsub.c
new file mode 100644 (file)
index 0000000..7a9bb7f
--- /dev/null
@@ -0,0 +1,1307 @@
+/*****************************************************************************
+ * dvbsub.c : DVB subtitles decoder thread
+ *****************************************************************************
+ * Copyright (C) 2003 ANEVIA
+ * $Id: dvbsub.c,v 1.1 2003/11/06 16:37:19 nitrox Exp $
+ *
+ * Authors: Damien LUCAS <damien.lucas@anevia.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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <stdlib.h>                                      /* malloc(), free() */
+#include <string.h>                                    /* memcpy(), memset() */
+#include <vlc/vlc.h>
+#include <vlc/vout.h>
+#include <vlc/decoder.h>
+#include "codecs.h"
+
+
+// Wow, that's ugly but very usefull for a memory leak track
+// so I just keep it
+#if 0
+static long long unsigned int trox_malloc_nb = 0;
+static long long unsigned int trox_free_nb = 0;
+
+static void* trox_malloc (size_t size)
+{ ++trox_malloc_nb; return malloc (size); }
+
+static void trox_free (void* ptr)
+{ ++trox_free_nb; free(ptr); return; }
+
+static void trox_call ()
+{
+  fprintf(stderr, "dvbbsub -- Memory usage:  %llu mallocs %llu frees (%llu)\n",
+                  trox_malloc_nb,
+                  trox_free_nb,
+                  trox_malloc_nb - trox_free_nb);
+  return;
+}
+#else
+# define trox_malloc malloc
+# define trox_free free
+# define trox_call()
+#endif
+/****************************************************************************
+ * Local structures
+ ****************************************************************************
+ * Those structures refer closely to the ETSI 300 743 Object model
+ ****************************************************************************/
+
+/* Storage of a RLE entry */
+typedef struct dvbsub_rle_s
+{
+    uint16_t                 i_num;
+    uint8_t                 i_color_code;
+    uint8_t                 y;
+    uint8_t                 cr;
+    uint8_t                 cb;
+    uint8_t                 t;
+    struct dvbsub_rle_s*    p_next;
+} dvbsub_rle_t;
+
+/* A subpicture image is a list of codes
+ * We need to store the length of each line since nothing specify in
+ * the standard that all lines shoudl have the same length
+ * WARNING: We assume here that a spu is less than 576 lines high */
+typedef struct
+{
+    uint16_t                        i_rows;
+    uint16_t                        i_cols[576];
+    dvbsub_rle_t*                   p_last;
+    dvbsub_rle_t*                   p_codes;
+} dvbsub_image_t;
+
+/* The object definition gives the position of the object in a region */
+typedef struct dvbsub_objectdef_s
+{
+    uint16_t                    i_id;
+    uint8_t                     i_type;
+    uint8_t                     i_provider;
+    uint16_t                    i_xoffset;
+    uint16_t                    i_yoffset;
+    uint8_t                     i_fg_pc;
+    uint8_t                     i_bg_pc;
+    struct dvbsub_objectdef_s*  p_next;
+} dvbsub_objectdef_t;
+
+/* The Region is an aera on the image
+ * with a list of the object definitions associated
+ * and a CLUT */
+typedef struct dvbsub_region_s
+{
+    uint8_t                 i_id;
+    uint8_t                 i_version_number;
+    vlc_bool_t              b_fill;
+    uint16_t                i_x;
+    uint16_t                i_y;
+    uint16_t                i_width;
+    uint16_t                i_height;
+    uint8_t                 i_level_comp;
+    uint8_t                 i_depth;
+    uint8_t                 i_clut;
+    uint8_t                 i_8bp_code;
+    uint8_t                 i_4bp_code;
+    uint8_t                 i_2bp_code;
+    dvbsub_objectdef_t*    p_object;
+} dvbsub_region_t;
+
+/* The page defines the list of regions */
+typedef struct
+{
+    uint16_t              i_id;
+    uint8_t               i_timeout;
+    uint8_t               i_state;
+    uint8_t               i_version_number;
+    uint8_t               i_regions_number;
+    dvbsub_region_t*      regions;
+} dvbsub_page_t;
+
+/* An object is constituted of 2 images (for interleaving) */
+typedef struct dvbsub_object_s
+{
+    uint16_t                i_id;
+    uint8_t                 i_version_number;
+    uint8_t                 i_coding_method;
+    vlc_bool_t              b_non_modify_color;
+    dvbsub_image_t*         topfield;
+    dvbsub_image_t*         bottomfield;
+    struct dvbsub_object_s* p_next;
+} dvbsub_object_t;
+
+/* The entry in the palette CLUT */
+typedef struct
+{
+    uint8_t                 Y;
+    uint8_t                 Cr;
+    uint8_t                 Cb;
+    uint8_t                 T;
+} dvbsub_color_t;
+
+/* */
+typedef struct
+{
+    uint8_t                 i_id;
+    uint8_t                 i_version_number;
+    dvbsub_color_t          c_2b[0xff];
+    dvbsub_color_t          c_4b[0xff];
+    dvbsub_color_t          c_8b[0xff];
+} dvbsub_clut_t;
+
+typedef struct
+{
+    uint8_t                 i_x;
+    uint16_t                i_y;
+    dvbsub_image_t*         p_rle_top;
+    dvbsub_image_t*         p_rle_bot;
+} dvbsub_render_t;
+
+typedef struct
+{
+    dvbsub_clut_t*          p_clut[0xff];
+    dvbsub_page_t*          p_page;
+    dvbsub_object_t*        p_objects;
+    subpicture_t*           p_spu[16];
+} dvbsub_all_t;
+typedef struct
+{
+    /* Thread properties and locks */
+    vlc_thread_t        thread_id;                /* Id for thread functions */
+    /* Input properties */
+    decoder_fifo_t*     p_fifo;                /* Stores the PES stream data */
+    bit_stream_t        bit_stream;             /* PES data at the bit level */
+    /* Output properties */
+    vout_thread_t*      p_vout;          /* Needed to create the subpictures */
+} dvbsub_thread_t;
+struct subpicture_sys_t
+{
+    mtime_t         i_pts;
+    void *          p_data;                          /* rle datas are stored */
+    vlc_object_t*   p_input;                            /* Link to the input */
+    vlc_bool_t      b_obsolete;
+};
+// List of different SEGMENT TYPES
+// According to EN 300-743, table 2
+#define DVBSUB_ST_PAGE_COMPOSITION      0x10
+#define DVBSUB_ST_REGION_COMPOSITION    0x11
+#define DVBSUB_ST_CLUT_DEFINITION       0x12
+#define DVBSUB_ST_OBJECT_DATA           0x13
+#define DVBSUB_ST_ENDOFDISPLAY          0x80
+#define DVBSUB_ST_STUFFING              0xff
+// List of different OBJECT TYPES
+// According to EN 300-743, table 6
+#define DVBSUB_OT_BASIC_BITMAP          0x00
+#define DVBSUB_OT_BASIC_CHAR            0x01
+#define DVBSUB_OT_COMPOSITE_STRING      0x02
+// Pixel DATA TYPES
+// According to EN 300-743, table 9
+#define DVBSUB_DT_2BP_CODE_STRING       0x10
+#define DVBSUB_DT_4BP_CODE_STRING       0x11
+#define DVBSUB_DT_8BP_CODE_STRING       0x12
+#define DVBSUB_DT_24_TABLE_DATA         0x20
+#define DVBSUB_DT_28_TABLE_DATA         0x21
+#define DVBSUB_DT_48_TABLE_DATA         0x22
+#define DVBSUB_DT_END_LINE              0xf0
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int  OpenDecoder   ( vlc_object_t * );
+static int  RunDecoder    ( decoder_fifo_t * );
+static int  InitThread    ( dvbsub_thread_t * );
+static void EndThread     ( dvbsub_thread_t *i, dvbsub_all_t*);
+static vout_thread_t *FindVout( dvbsub_thread_t * );
+static void RenderI42x( vout_thread_t *, picture_t *, const subpicture_t *,
+                        vlc_bool_t );
+static void RenderYUY2( vout_thread_t *, picture_t *, const subpicture_t *,
+                        vlc_bool_t );
+static void dvbsub_clut_add_entry ( dvbsub_clut_t* clut, uint8_t type,
+                                    uint8_t id, uint8_t y, uint8_t cr,
+                                    uint8_t cb, uint8_t t);
+static void dvbsub_add_objectdef_to_region ( dvbsub_objectdef_t* p_obj,
+                                   dvbsub_region_t* p_region );
+static dvbsub_image_t* dvbsub_parse_pdata ( dvbsub_thread_t* ,uint16_t );
+static uint16_t dvbsub_count0x11(dvbsub_thread_t* p_spudec,
+                                 uint16_t* p,
+                                 dvbsub_image_t* p_image);
+static void dvbsub_decode_segment ( dvbsub_thread_t *, dvbsub_all_t* );
+static void dvbsub_decode_page_composition ( dvbsub_thread_t *, dvbsub_all_t* );
+static void dvbsub_decode_region_composition ( dvbsub_thread_t*, dvbsub_all_t*);
+static void dvbsub_decode_object ( dvbsub_thread_t*, dvbsub_all_t* );
+static vlc_bool_t dvbsub_check_page ( dvbsub_all_t* );
+static void dvbsub_render ( dvbsub_thread_t *p_spudec,dvbsub_all_t* );
+static int dvbsub_parse ( dvbsub_thread_t *p_spudec, dvbsub_all_t* dvbsub );
+static void dvbsub_decode_clut ( dvbsub_thread_t*, dvbsub_all_t*);
+static void dvbsub_stop_display ( dvbsub_thread_t* p_dec, dvbsub_all_t* dvbsub);
+
+static void free_image (dvbsub_image_t* p_i);
+static void free_object (dvbsub_object_t* p_o);
+static void free_regions (dvbsub_region_t* p_r, uint8_t nb);
+static void free_objects (dvbsub_object_t* p_o);
+static void free_clut ( dvbsub_clut_t* p_c);
+static void free_page (dvbsub_page_t* p_p);
+static void free_all ( dvbsub_all_t* p_a );
+
+
+/*****************************************************************************
+ * Module descriptor.
+ *****************************************************************************/
+vlc_module_begin();
+    add_category_hint( N_("subtitles"), NULL, VLC_TRUE );
+    set_description( _("subtitles decoder") );
+    set_capability( "decoder", 50 );
+    set_callbacks( OpenDecoder, NULL );
+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;
+    if( p_dec->p_fifo->i_fourcc != VLC_FOURCC('d','v','b','s') )
+    {
+        return VLC_EGENERIC;
+    }
+    p_dec->pf_run = RunDecoder;
+    return VLC_SUCCESS;
+}
+/*****************************************************************************
+ * RunDecoder: this function is called just after the thread is created
+ *****************************************************************************/
+static int RunDecoder( decoder_fifo_t * p_fifo )
+{
+    dvbsub_thread_t *    p_dvbsubdec;
+//    vout_thread_t *         p_vout_backup = NULL;
+    dvbsub_all_t            dvbsub;
+    unsigned int            k;
+    /* Allocate the memory needed to store the thread's structure */
+    p_dvbsubdec = (dvbsub_thread_t *)trox_malloc( sizeof(dvbsub_thread_t) );
+    if ( p_dvbsubdec == NULL )
+    {
+        msg_Err( p_fifo, "out of memory" );
+        DecoderError( p_fifo );
+        return( -1 );
+    }
+    /*
+     * Initialize the thread properties
+     */
+    p_dvbsubdec->p_vout = NULL;
+    p_dvbsubdec->p_fifo = p_fifo;
+    /*
+     * Initialize thread and free configuration
+     */
+    p_dvbsubdec->p_fifo->b_error = InitThread( p_dvbsubdec );
+    dvbsub.p_page=NULL;
+    dvbsub.p_objects=NULL;
+    for(k=0; k<0xff; k++) dvbsub.p_clut[k] = NULL;
+    for(k=0; k<16; k++) dvbsub.p_spu[k] = NULL;
+
+    /*
+     * Main loop - it is not executed if an error occured during
+     * initialization
+     */
+    while( (!p_dvbsubdec->p_fifo->b_die) && (!p_dvbsubdec->p_fifo->b_error) )
+    {
+        dvbsub_parse( p_dvbsubdec, &dvbsub );
+        p_dvbsubdec->p_vout = FindVout( p_dvbsubdec );
+        if( p_dvbsubdec->p_vout )
+        {
+            // Check if the page is to be displayed
+            if(dvbsub_check_page(&dvbsub))
+            {
+                dvbsub_render(p_dvbsubdec, &dvbsub);
+            }
+            vlc_object_release( p_dvbsubdec->p_vout );
+        }
+    }
+    // Free all structures
+    //dvbsub.p_objects=NULL;
+    //for(k=0; k<16; k++)
+    //    if(dvbsub.p_spu[k] != NULL)
+    //        dvbsub.p_spu[k]->p_sys->b_obsolete = 1;
+
+    /*
+     * Error loop
+     */
+    if( p_dvbsubdec->p_fifo->b_error )
+    {
+        DecoderError( p_dvbsubdec->p_fifo );
+        /* End of thread */
+        EndThread( p_dvbsubdec, &dvbsub );
+        return -1;
+    }
+    /* End of thread */
+    EndThread( p_dvbsubdec, &dvbsub );
+    free_all(&dvbsub);
+    return 0;
+}
+/* following functions are local */
+/*****************************************************************************
+ * InitThread: initialize dvbsub decoder thread
+ *****************************************************************************
+ * This function is called from RunThread and performs the second step of the
+ * initialization. It returns 0 on success. Note that the thread's flag are not
+ * modified inside this function.
+ *****************************************************************************/
+static int InitThread( dvbsub_thread_t *p_dvbsubdec )
+{
+    int i_ret;
+    /* Call InitBitstream anyway so p_spudec->bit_stream is in a known
+     * state before calling CloseBitstream */
+    i_ret = InitBitstream( &p_dvbsubdec->bit_stream, p_dvbsubdec->p_fifo,
+                           NULL, NULL );
+    /* Check for a video output */
+    p_dvbsubdec->p_vout = FindVout( p_dvbsubdec );
+    if( !p_dvbsubdec->p_vout )
+    {
+        return -1;
+    }
+    /* It was just a check */
+    vlc_object_release( p_dvbsubdec->p_vout );
+    p_dvbsubdec->p_vout = NULL;
+    return i_ret;
+}
+/*****************************************************************************
+ * FindVout: Find a vout or wait for one to be created.
+ *****************************************************************************/
+static vout_thread_t *FindVout( dvbsub_thread_t *p_spudec )
+{
+    vout_thread_t *p_vout = NULL;
+    /* Find an available video output */
+    do
+    {
+        if( p_spudec->p_fifo->b_die || p_spudec->p_fifo->b_error )
+        {
+            break;
+        }
+        p_vout = vlc_object_find( p_spudec->p_fifo, VLC_OBJECT_VOUT,
+                                  FIND_ANYWHERE );
+        if( p_vout )
+        {
+            break;
+        }
+        msleep( VOUT_OUTMEM_SLEEP );
+    }
+    while( 1 );
+    return p_vout;
+}
+/*****************************************************************************
+ * EndThread: thread destruction
+ *****************************************************************************
+ * This function is called when the thread ends after a sucessful
+ * initialization.
+ *****************************************************************************/
+static void EndThread( dvbsub_thread_t *p_dvbsubdec, dvbsub_all_t* p_dvbsub )
+{
+    if( p_dvbsubdec->p_vout != NULL
+         && p_dvbsubdec->p_vout->p_subpicture != NULL )
+    {
+        subpicture_t *  p_subpic;
+        int i_subpic;
+        for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
+        {
+            p_subpic = &p_dvbsubdec->p_vout->p_subpicture[i_subpic];
+            if( p_subpic != NULL &&
+              ( ( p_subpic->i_status == RESERVED_SUBPICTURE )
+             || ( p_subpic->i_status == READY_SUBPICTURE ) ) )
+            {
+                vout_DestroySubPicture( p_dvbsubdec->p_vout, p_subpic );
+            }
+        }
+    }
+    CloseBitstream( &p_dvbsubdec->bit_stream );
+    trox_free( p_dvbsubdec );
+    trox_call();
+}
+
+
+static int dvbsub_parse ( dvbsub_thread_t *p_spudec,
+                          dvbsub_all_t* dvbsub )
+{
+    unsigned int data_identifier;
+    unsigned int subtitle_stream_id;
+    unsigned int nextbits;
+    uint32_t end_data_marker;
+    /* Re-align the buffer on an 8-bit boundary */
+    RealignBits( &p_spudec->bit_stream );
+    data_identifier = GetBits( &p_spudec->bit_stream, 8 );
+    subtitle_stream_id = GetBits( &p_spudec->bit_stream, 8 );
+    nextbits = ShowBits( &p_spudec->bit_stream, 8 );
+    while(nextbits == 0x0f )
+    {
+        dvbsub_decode_segment(  p_spudec, dvbsub );
+        nextbits = ShowBits( &p_spudec->bit_stream, 8 );
+    }
+    end_data_marker = GetBits( &p_spudec->bit_stream, 8 );
+    return 0;
+}
+
+
+
+static void dvbsub_decode_segment ( dvbsub_thread_t * p_spudec,
+                                    dvbsub_all_t * dvbsub )
+{
+    unsigned int sync_byte;
+    unsigned int segment_type;
+    uint16_t page_id;
+    uint16_t segment_length;
+    int k;
+    sync_byte = GetBits( &p_spudec->bit_stream, 8 );
+    segment_type = GetBits( &p_spudec->bit_stream, 8 );
+    page_id = GetBits( &p_spudec->bit_stream, 16 );
+    segment_length = ShowBits( &p_spudec->bit_stream, 16 );
+    if( page_id != ((dvb_spuinfo_t*)p_spudec->p_fifo->p_spuinfo)->i_id )
+    {
+        //TODO should use GetChunk
+        for(k=0; k<segment_length+2; k++) GetBits( &p_spudec->bit_stream, 8 );
+        return;
+    }
+    switch( segment_type )
+    {
+        case DVBSUB_ST_CLUT_DEFINITION:
+            dvbsub_decode_clut ( p_spudec, dvbsub );
+            break;
+         case DVBSUB_ST_PAGE_COMPOSITION:
+            dvbsub_decode_page_composition ( p_spudec, dvbsub );
+            break;
+        case DVBSUB_ST_REGION_COMPOSITION:
+            dvbsub_decode_region_composition ( p_spudec, dvbsub );
+            break;
+        case DVBSUB_ST_OBJECT_DATA:
+            dvbsub_decode_object (  p_spudec, dvbsub );
+            break;
+        case DVBSUB_ST_ENDOFDISPLAY:
+            dvbsub_stop_display ( p_spudec, dvbsub);
+            break;
+        case DVBSUB_ST_STUFFING:
+        default:
+            fprintf(stderr, "*** DVBSUB - Unsupported segment type ! (%04x)\n",
+                                                                segment_type );
+            GetBits( &p_spudec->bit_stream, 16 );
+            for(k=0; k<segment_length; k++)
+                  GetBits( &p_spudec->bit_stream, 8 );
+            break;
+    }
+    return;
+}
+
+
+static void dvbsub_decode_page_composition (dvbsub_thread_t *p_spudec,
+                                            dvbsub_all_t *dvbsub)
+{
+    unsigned int i_version_number;
+    unsigned int i_state;
+    unsigned int i_segment_length;
+    uint8_t i_timeout;
+    unsigned int k;
+    i_segment_length = GetBits( &p_spudec->bit_stream, 16 );
+    //A page is composed by one or more region:
+    i_timeout =  GetBits( &p_spudec->bit_stream, 8 );
+    i_version_number =  GetBits( &p_spudec->bit_stream, 4 );
+    i_state =  GetBits( &p_spudec->bit_stream, 2 );
+    // TODO We assume it is a new page (i_state)
+    if (dvbsub->p_page) free_page(dvbsub->p_page);
+
+    GetBits( &p_spudec->bit_stream, 2 ); /* Reserved */
+    //Allocate a new page
+    dvbsub->p_page = trox_malloc (sizeof(dvbsub_page_t));
+    dvbsub->p_page->i_timeout = i_timeout;
+    // Number of regions:
+    dvbsub->p_page->i_regions_number = (i_segment_length-2) / 6;
+
+/* Special workaround for CAVENA encoders
+ * a page with no regions is sent instead of a 0x80 packet (End Of Display) */
+    if( dvbsub->p_page->i_regions_number == 0 )
+    {
+        dvbsub_stop_display(p_spudec, dvbsub );
+    }
+/* /Special workaround */
+
+    dvbsub->p_page->regions =
+               trox_malloc(dvbsub->p_page->i_regions_number*sizeof(dvbsub_region_t));
+    for(k=0; k<dvbsub->p_page->i_regions_number ; k++)
+    {
+        dvbsub->p_page->regions[k].i_id = GetBits( &p_spudec->bit_stream, 8 );
+        GetBits( &p_spudec->bit_stream, 8 ); /* Reserved */
+        dvbsub->p_page->regions[k].i_x = GetBits( &p_spudec->bit_stream, 16 );
+        dvbsub->p_page->regions[k].i_y = GetBits( &p_spudec->bit_stream, 16 );
+        dvbsub->p_page->regions[k].p_object = NULL;
+    }
+}
+
+
+static void dvbsub_decode_region_composition (dvbsub_thread_t *p_spudec,
+                                       dvbsub_all_t *dvbsub)
+{
+    unsigned int i_segment_length;
+    unsigned int i_processed_length;
+    unsigned int i_region_id;
+    dvbsub_region_t* p_region;
+    unsigned int k;
+    p_region = NULL;
+    i_segment_length = GetBits( &p_spudec->bit_stream, 16 );
+    // Get region id:
+    i_region_id = GetBits( &p_spudec->bit_stream, 8 );
+    for(k=0; k<dvbsub->p_page->i_regions_number; k++)
+    {
+        if ( dvbsub->p_page->regions[k].i_id ==  i_region_id )
+          p_region = &(dvbsub->p_page->regions[k]);
+    }
+    if(p_region == NULL)
+    {
+        // TODO
+        // The region has never been declared before
+        // Internal error
+        fprintf (stderr, "Decoding of undeclared region N/A...\n");
+        return;
+    }
+    // Skip version number and fill flag
+    if (ShowBits( &p_spudec->bit_stream, 4 ) == p_region->i_version_number)
+    {
+        fprintf(stderr, "Skipping already known region N/A ...\n");
+        // TODO Skip the right number of bits
+    }
+    // Region attributes
+    p_region->i_version_number = GetBits( &p_spudec->bit_stream, 4 );
+    p_region->b_fill = GetBits( &p_spudec->bit_stream, 1 );
+    GetBits( &p_spudec->bit_stream, 3 ); /* Reserved */
+    p_region->i_width = GetBits( &p_spudec->bit_stream, 16 );
+    p_region->i_height =  GetBits( &p_spudec->bit_stream, 16 );
+    p_region->i_level_comp =  GetBits( &p_spudec->bit_stream, 3 );
+    p_region->i_depth =  GetBits( &p_spudec->bit_stream, 3 );
+    GetBits( &p_spudec->bit_stream, 2 ); /* Reserved */
+    p_region->i_clut =  GetBits( &p_spudec->bit_stream, 8 );
+    p_region->i_8bp_code = GetBits( &p_spudec->bit_stream, 8 );
+    p_region->i_4bp_code = GetBits( &p_spudec->bit_stream, 4 );
+    p_region->i_2bp_code = GetBits( &p_spudec->bit_stream, 2 );
+    GetBits( &p_spudec->bit_stream, 2 ); /* Reserved */
+    // List of objects in the region:
+    // We already skipped 10 bytes
+    i_processed_length = 10;
+    while ( i_processed_length < i_segment_length )
+    {
+        // We create a new object
+        dvbsub_objectdef_t*     p_obj;
+        p_obj = trox_malloc(sizeof(dvbsub_objectdef_t));
+        // We parse object properties
+        p_obj->i_id = GetBits( &p_spudec->bit_stream, 16 );
+        p_obj->i_type = GetBits( &p_spudec->bit_stream, 2 );
+        p_obj->i_provider = GetBits( &p_spudec->bit_stream, 2 );
+        p_obj->i_xoffset = GetBits( &p_spudec->bit_stream, 12 );
+        GetBits( &p_spudec->bit_stream, 4 ); /* Reserved */
+        p_obj->i_yoffset = GetBits( &p_spudec->bit_stream, 12 );
+        i_processed_length += 6;
+        if ( p_obj->i_type == DVBSUB_OT_BASIC_CHAR 
+               || p_obj->i_type == DVBSUB_OT_COMPOSITE_STRING )
+        {
+            p_obj->i_fg_pc =  GetBits( &p_spudec->bit_stream, 8 );
+            p_obj->i_bg_pc =  GetBits( &p_spudec->bit_stream, 8 );
+            i_processed_length += 2;
+        }
+        p_obj->p_next = NULL;
+        dvbsub_add_objectdef_to_region(p_obj, p_region);
+    }
+}
+
+
+static void dvbsub_decode_object (dvbsub_thread_t* p_spudec,
+                                  dvbsub_all_t* dvbsub)
+{
+    dvbsub_object_t*   p_obj;
+    dvbsub_object_t*   p_o;
+    uint16_t    i_segment_length;
+    uint16_t    i_topfield_length;
+    uint16_t    i_bottomfield_length;
+    // Memory Allocation
+    p_obj = trox_malloc ( sizeof ( dvbsub_object_t ) );
+    p_obj->p_next=NULL;
+    i_segment_length =  GetBits( &p_spudec->bit_stream, 16 );
+    p_obj->i_id =  GetBits( &p_spudec->bit_stream, 16 );
+    p_obj->i_version_number = GetBits( &p_spudec->bit_stream, 4 );
+    // TODO Check we don't already have this object / this version
+    p_obj->i_coding_method = GetBits( &p_spudec->bit_stream, 2 );
+    p_obj->b_non_modify_color = GetBits( &p_spudec->bit_stream, 1 );
+    GetBits( &p_spudec->bit_stream, 1 ); /* Reserved */
+    if(p_obj->i_coding_method == 0x00)
+    {
+        i_topfield_length = GetBits( &p_spudec->bit_stream, 16 );
+        i_bottomfield_length = GetBits( &p_spudec->bit_stream, 16 );
+        p_obj->topfield = dvbsub_parse_pdata (p_spudec, i_topfield_length);
+        p_obj->bottomfield =
+                            dvbsub_parse_pdata (p_spudec, i_bottomfield_length);
+    }
+    else
+    {
+        GetBits(&p_spudec->bit_stream, (i_segment_length -3) *8);
+        //TODO
+        // DVB subtitling as characters
+    }
+    // Add this object to the list of the page
+    p_o = dvbsub->p_objects;
+    dvbsub->p_objects = p_obj;
+    p_obj->p_next = p_o;
+    return;
+}
+
+static void dvbsub_stop_display ( dvbsub_thread_t* p_dec,
+                                  dvbsub_all_t* dvbsub)
+{
+    unsigned int j;
+
+    for(j = 0; dvbsub->p_spu[j] != NULL; j++)
+        dvbsub->p_spu[j]->i_stop = p_dec->bit_stream.p_pes->i_pts;
+    return;
+}
+
+static void dvbsub_decode_clut ( dvbsub_thread_t* p_dec,
+                                 dvbsub_all_t* dvbsub)
+{
+    uint16_t         i_segment_length;
+    uint16_t         i_processed_length;
+    uint8_t          i_entry_id;
+    uint8_t          i_entry_type;
+    dvbsub_clut_t*   clut;
+    uint8_t          i_clut_id;
+    uint8_t          i_version_number;
+    uint8_t          y;
+    uint8_t          cr;
+    uint8_t          cb;
+    uint8_t          t;
+    i_segment_length =  GetBits( &p_dec->bit_stream, 16 );
+    i_clut_id = GetBits( &p_dec->bit_stream, 8 );
+    i_version_number = GetBits( &p_dec->bit_stream, 4 );
+    // Check that this id doesn't not already exist
+    // with the same version number
+    // And allocate memory if necessary
+    if( dvbsub->p_clut[i_clut_id] != NULL)
+    {
+        if ( dvbsub->p_clut[i_clut_id]->i_version_number == i_version_number )
+        {
+            //TODO skip the right number of bits
+            return;
+        }
+        else
+        {
+            memset(dvbsub->p_clut[i_clut_id], 0, sizeof(dvbsub_clut_t));
+        }
+    }
+    else
+    {
+        dvbsub->p_clut[i_clut_id] = trox_malloc(sizeof(dvbsub_clut_t));
+    }
+    clut = dvbsub->p_clut[i_clut_id];
+    /* We don't have this version of the CLUT:
+     * Parse it                                 */
+    clut->i_version_number = i_version_number;
+    GetBits( &p_dec->bit_stream, 4 ); /* Reserved bits */
+    i_processed_length=2;
+    while(i_processed_length<i_segment_length)
+    {
+        i_entry_id = GetBits( &p_dec->bit_stream, 8 );
+        i_entry_type = GetBits( &p_dec->bit_stream, 3 );
+        GetBits( &p_dec->bit_stream, 4 );
+        if ( GetBits( &p_dec->bit_stream, 1 )==0x01 )
+        {
+                y  = GetBits( &p_dec->bit_stream, 8 );
+                cr = GetBits( &p_dec->bit_stream, 8 );
+                cb = GetBits( &p_dec->bit_stream, 8 );
+                t  = GetBits( &p_dec->bit_stream, 8 );
+                i_processed_length += 6;
+        }
+        else
+        {
+                y  = GetBits( &p_dec->bit_stream, 6 );
+                cr = GetBits( &p_dec->bit_stream, 4 );
+                cb = GetBits( &p_dec->bit_stream, 4 );
+                t  = GetBits( &p_dec->bit_stream, 2 );
+                i_processed_length += 4;
+        }
+        dvbsub_clut_add_entry(clut, i_entry_type, i_entry_id, y, cr, cb, t);
+    }
+}
+
+
+static void dvbsub_clut_add_entry ( dvbsub_clut_t* clut, uint8_t type,
+                                    uint8_t id, uint8_t y, uint8_t cr,
+                                    uint8_t cb, uint8_t t)
+{
+    /* According to EN 300-743 section 7.2.3 note 1, type should
+     * not have more than 1 bit set to one
+       But, some strams don't respect this note. */
+    if( type & 0x04)
+    {
+        clut->c_2b[id].Y = y;
+        clut->c_2b[id].Cr = cr;
+        clut->c_2b[id].Cb = cb;
+        clut->c_2b[id].T = t;
+    }
+    if( type & 0x02)
+    {
+        clut->c_4b[id].Y = y;
+        clut->c_4b[id].Cr = cr;
+        clut->c_4b[id].Cb = cb;
+        clut->c_4b[id].T = t;
+    }
+    if( type & 0x01)
+    {
+        clut->c_8b[id].Y = y;
+        clut->c_8b[id].Cr = cr;
+        clut->c_8b[id].Cb = cb;
+        clut->c_8b[id].T = t;
+    }
+    return;
+}
+
+
+static void dvbsub_add_objectdef_to_region ( dvbsub_objectdef_t* p_obj,
+                                   dvbsub_region_t* p_region )
+{
+    dvbsub_objectdef_t* p_o = p_region->p_object;
+    // Seek to the last non null element
+    if(p_o!=NULL)
+    {
+        for(; p_o->p_next!=NULL; p_o=p_o->p_next);
+        p_o->p_next = p_obj;
+        p_o->p_next->p_next = NULL;
+    }
+    else
+    {
+        p_region->p_object = p_obj;
+        p_region->p_object->p_next = NULL;
+    }
+    return;
+}
+
+
+static dvbsub_image_t* dvbsub_parse_pdata ( dvbsub_thread_t* p_spudec,
+                                                   uint16_t length )
+{
+    dvbsub_image_t* p_image;
+    uint16_t i_processed_length=0;
+    uint16_t i_lines=0;
+    uint16_t i_cols_last=0;
+    p_image = trox_malloc ( sizeof ( dvbsub_image_t) );
+    p_image->p_last=NULL;
+    memset(p_image->i_cols, 0, 576*sizeof(uint16_t));
+    /* Let's parse it a first time to determine the size of the buffer */
+    while (i_processed_length < length)
+    {
+        switch(GetBits( &p_spudec->bit_stream, 8 ))
+        {
+            case 0x10:
+                fprintf(stderr, "0x10 N/A\n");
+                break;
+            case 0x11:
+                i_processed_length += 1 + dvbsub_count0x11(p_spudec,
+                                                &(p_image->i_cols[i_lines]),
+                                                p_image);
+                break;
+            case 0x12:
+                fprintf(stderr, "0x12 N/A\n");
+                break;
+            case 0x20:
+                fprintf(stderr, "0x20 N/A\n");
+                break;
+            case 0x21:
+                fprintf(stderr, "0x21 N/A\n");
+                break;
+            case 0x22:
+                fprintf(stderr, "0x22 N/A\n");
+                break;
+            case 0xf0:
+                i_processed_length++;
+                i_lines++;
+                break;
+        }
+    }
+    p_image->i_rows =  i_lines;
+    p_image->i_cols[i_lines] = i_cols_last;
+    // Check word-aligned bits
+    if(ShowBits( &p_spudec->bit_stream, 8 )==0x00)
+        GetBits( &p_spudec->bit_stream, 8 );
+    return p_image;
+}
+
+
+
+static void add_rle_code (dvbsub_image_t* p, uint16_t num, uint8_t color)
+{
+    if(p->p_last != NULL)
+    {
+        p->p_last->p_next = trox_malloc (sizeof (dvbsub_rle_t));
+        p->p_last = p->p_last->p_next;
+   }
+    else
+    {
+        p->p_codes =  trox_malloc (sizeof (dvbsub_rle_t));
+        p->p_last = p->p_codes;
+    }
+    p->p_last->i_num = num;
+    p->p_last->i_color_code = color;
+    p->p_last->p_next = NULL;
+    return;
+}
+
+
+static uint16_t dvbsub_count0x11(dvbsub_thread_t* p_spudec, uint16_t* p, dvbsub_image_t* p_image)
+{
+    uint16_t i_processed=0;
+    vlc_bool_t b_stop=0;
+    uint16_t i_count = 0;
+    uint8_t i_color =0;
+    while (!b_stop)
+    {
+        if ( (i_color = GetBits( &p_spudec->bit_stream, 4 )) != 0x00 )
+        {
+            (*p)++;
+            i_processed+=4;
+            // 1 pixel of color code '0000'
+            add_rle_code (p_image, 1, i_color );
+        }
+        else
+        {
+            if(GetBits( &p_spudec->bit_stream, 1 ) == 0x00)           // Switch1
+            {
+                if( ShowBits( &p_spudec->bit_stream, 3 ) != 0x00 )
+                {
+                    i_count = 2 + GetBits( &p_spudec->bit_stream, 3 );
+                    (*p) += i_count ;
+                    add_rle_code (p_image, i_count, 0x00);
+                }
+                else
+                {
+                    GetBits( &p_spudec->bit_stream, 3);
+                    b_stop=1;
+                }
+                i_processed += 8;
+            }
+            else
+            {
+                if(GetBits( &p_spudec->bit_stream, 1 ) == 0x00)        //Switch2
+                {
+                    i_count =  4 + GetBits( &p_spudec->bit_stream, 2 );
+                    i_color = GetBits(  &p_spudec->bit_stream, 4 );
+                    (*p) += i_count;
+                    i_processed += 12;
+                    add_rle_code(p_image, i_count, i_color);
+                }
+                else
+                {
+                    switch ( GetBits( &p_spudec->bit_stream, 2 ) )     //Switch3
+                    {
+                        case 0x0:
+                            (*p)++;
+                            i_processed += 8;
+                            add_rle_code(p_image, 1, 0x00);
+                            break;
+                        case 0x1:
+                            (*p)+=2;
+                            i_processed += 8;
+                            add_rle_code(p_image, 2, 0x00);
+                            break;
+                        case 0x2:
+                             i_count = 9 + GetBits( &p_spudec->bit_stream, 4 );
+                             i_color = GetBits( &p_spudec->bit_stream, 4 );
+                             (*p)+= i_count;
+                             i_processed += 16;
+                             add_rle_code ( p_image, i_count, i_color );
+                             break;
+                        case 0x3:
+                             i_count= 25 + GetBits( &p_spudec->bit_stream, 8 );
+                             i_color = GetBits( &p_spudec->bit_stream, 4 );
+                             (*p)+= i_count;
+                             i_processed += 20;
+                             add_rle_code ( p_image, i_count, i_color );
+                             break;
+                    }
+                }
+            }
+        }
+    }
+    RealignBits (  &p_spudec->bit_stream );
+    return (i_processed+7)/8 ;
+}
+
+static vlc_bool_t dvbsub_check_page(dvbsub_all_t* dvbsub)
+{
+    if(dvbsub->p_page != NULL)
+    {
+        if(dvbsub->p_objects != NULL)
+            return VLC_TRUE;
+    }
+    return VLC_FALSE;
+}
+
+static void free_image (dvbsub_image_t* p_i)
+{
+    dvbsub_rle_t* p1;
+    dvbsub_rle_t* p2=NULL;
+
+    for( p1 = p_i->p_codes; p1 != NULL; p1=p2)
+    {
+        p2=p1->p_next;
+        trox_free(p1);
+        p1=NULL;
+    }
+
+    trox_free(p_i);
+}
+
+static void free_object (dvbsub_object_t* p_o)
+{
+    trox_free(p_o);
+}
+
+static void free_objectdefs ( dvbsub_objectdef_t* p_o)
+{
+    dvbsub_objectdef_t* p1;
+    dvbsub_objectdef_t* p2=NULL;
+
+    for( p1 = p_o; p1 != NULL; p1=p2)
+    {
+        p2=p1->p_next;
+        trox_free(p1);
+        p1=NULL;
+    }
+}
+
+static void free_regions (dvbsub_region_t* p_r, uint8_t nb)
+{
+    unsigned int i;
+
+    for (i = 0; i<nb; i++) free_objectdefs ( p_r[i].p_object );
+    trox_free (p_r);
+    p_r = NULL;
+}
+
+static void free_objects (dvbsub_object_t* p_o)
+{
+    dvbsub_object_t* p1;
+    dvbsub_object_t* p2=NULL;
+
+    for( p1 = p_o; p1 != NULL; p1=p2)
+    {
+        p2=p1->p_next;
+        free_image (p1->topfield);
+        free_image (p1->bottomfield);
+        free_object(p1);
+    }
+}
+
+static void free_clut ( dvbsub_clut_t* p_c) { trox_free(p_c); }
+
+static void free_page (dvbsub_page_t* p_p)
+{
+    free_regions (p_p->regions, p_p->i_regions_number);
+    trox_free(p_p);
+    p_p = NULL;
+}
+
+static void free_spu( subpicture_t *p_spu )
+{
+    if ( p_spu->p_sys )
+    {
+        free_image(((dvbsub_render_t *)p_spu->p_sys->p_data)->p_rle_top);
+        free_image(((dvbsub_render_t *)p_spu->p_sys->p_data)->p_rle_bot);
+        trox_free(p_spu->p_sys->p_data);
+        trox_free( p_spu->p_sys );
+        p_spu->p_sys = NULL;
+    }
+}
+
+static void free_all ( dvbsub_all_t* p_a )
+{
+    unsigned int i;
+
+    for(i=0; i<0xff; i++) if (p_a->p_clut[i]) free_clut ( p_a->p_clut[i] );
+    for(i=0; i<16; i++) if (p_a->p_spu[i]) free_spu ( p_a->p_spu[i] );
+    if(p_a->p_page) free_page( p_a->p_page );
+    free_objects (p_a->p_objects);
+
+}
+
+static void dvbsub_RenderDVBSUB ( vout_thread_t *p_vout, picture_t *p_pic,
+                                const subpicture_t *p_spu, vlc_bool_t b_crop )
+{
+    // If we have changed the language on the fly,
+    if(!p_spu->p_sys) return;
+
+    if(p_spu->p_sys->b_obsolete) return;
+
+    switch (p_vout->output.i_chroma)
+    {
+        /* I420 target, no scaling */
+        case VLC_FOURCC('I','4','2','2'):
+        case VLC_FOURCC('I','4','2','0'):
+        case VLC_FOURCC('I','Y','U','V'):
+        case VLC_FOURCC('Y','V','1','2'):
+            // As long as we just use Y info, I422 and YV12 are just equivalent
+            // to I420. Remember to change it the day we'll take into account
+            // U and V info.
+            RenderI42x( p_vout, p_pic, p_spu, VLC_FALSE );
+            break;
+        /* RV16 target, scaling */
+        case VLC_FOURCC('R','V','1','6'):
+            fprintf(stderr, "Not implemented chroma ! RV16)\n");
+            //RenderRV16( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
+            break;
+        /* RV32 target, scaling */
+        case VLC_FOURCC('R','V','2','4'):
+        case VLC_FOURCC('R','V','3','2'):
+            fprintf(stderr, "Not implemented chroma ! RV32 \n");
+            //RenderRV32( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
+            break;
+        /* NVidia overlay, no scaling */
+        case VLC_FOURCC('Y','U','Y','2'):
+            RenderYUY2( p_vout, p_pic, p_spu, VLC_FALSE );
+            break;
+        default:
+            msg_Err( p_vout, "unknown chroma, can't render SPU" );
+            break;
+    }
+}
+
+
+static void RenderYUY2 ( vout_thread_t *p_vout, picture_t *p_pic,
+                        const subpicture_t *p_spu, vlc_bool_t b_crop )
+{
+    /* Common variables */
+    uint8_t  *p_desty;
+    uint16_t i,j;
+    uint16_t i_cnt;
+    uint16_t x, y;
+    dvbsub_rle_t* p_c;
+    dvbsub_render_t* p_r = ((dvbsub_render_t *)p_spu->p_sys->p_data);
+    dvbsub_image_t* p_im = p_r->p_rle_top;
+    i=0;
+    j=0;
+    p_desty = p_pic->Y_PIXELS;
+    //let's render the 1st frame
+    for(p_c = p_im->p_codes; p_c->p_next != NULL; p_c=p_c->p_next)
+    {
+//        if( p_c->y != 0  && p_c->t < 0x20)
+        if( p_c->y != 0  && p_c->t < 0x20)
+        {
+            x = j+ p_r->i_x;
+            y = 2*i+p_r->i_y;
+            //memset(p_desty+ y*p_pic->Y_PITCH + x, p_c->y, p_c->i_num);
+            // In YUY2 we have to set pixel per pixel
+            for( i_cnt = 0; i_cnt < p_c->i_num; i_cnt+=2 )
+            {
+                memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt, p_c->y, 1);
+           //     memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+1, p_c->cr, 1);
+          //      memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+2, p_c->y, 1);
+           //     memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+3, p_c->cb, 1);
+            }
+        }
+        j += p_c->i_num;
+        if(j >= p_im->i_cols[i])
+        {
+            i++; j=0;
+        }
+        if( i>= p_im->i_rows) break;
+    }
+    //idem for the second frame
+    p_im = p_r->p_rle_bot; i=0; j=0;
+    for(p_c = p_im->p_codes; p_c->p_next != NULL; p_c=p_c->p_next)
+    {
+        if( p_c->y != 0 && p_c->t < 0x20)
+        {
+            x = j+ p_r->i_x;
+            y = 2*i+1+p_r->i_y;
+            //memset(p_desty+ y*p_pic->Y_PITCH + x, p_c->y, p_c->i_num);
+            // In YUY2 we have to set pixel per pixel
+            for( i_cnt = 0; i_cnt < p_c->i_num; i_cnt+=2 )
+            {
+                memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt, p_c->y, 1);
+           //     memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+1, p_c->cr, 1);
+           //     memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+2, p_c->y, 1);
+           //     memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+3, p_c->cb, 1);
+           }
+        }
+        j += p_c->i_num;
+        if(j >= p_im->i_cols[i])
+        {
+            i++; j=0;
+        }
+        if( i>= p_im->i_rows) break;
+    }
+}
+
+
+static void RenderI42x ( vout_thread_t *p_vout, picture_t *p_pic,
+                        const subpicture_t *p_spu, vlc_bool_t b_crop )
+{
+    /* Common variables */
+    uint8_t  *p_desty;
+    uint8_t  *p_destu;
+    uint8_t  *p_destv;
+    uint16_t i,j;
+    uint16_t x, y;
+    dvbsub_rle_t* p_c;
+    dvbsub_render_t* p_r = ((dvbsub_render_t *)p_spu->p_sys->p_data);
+    dvbsub_image_t* p_im = p_r->p_rle_top;
+    i=0;
+    j=0;
+    p_desty = p_pic->Y_PIXELS;
+    p_destu = p_pic->U_PIXELS;
+    p_destv = p_pic->V_PIXELS;
+    //let's render the 1st frame
+    for(p_c = p_im->p_codes; p_c->p_next != NULL; p_c=p_c->p_next)
+    {
+        if( p_c->y != 0 )
+        {
+            x = j+ p_r->i_x;
+            y = 2*i+p_r->i_y;
+            //memset(p_dest+ y*p_pic->U_PITCH*2 + x, p_c->cr, p_c->i_num);
+//            memset(p_desty+ (y)*p_pic->Y_PITCH + x, p_c->cr, p_c->i_num);
+            //memset(p_dest+ y*p_pic->V_PITCH*2 + x, p_c->cb, p_c->i_num);
+            //memset(p_destu+ (y)*p_pic->Y_PITCH + x, p_c->cb, p_c->i_num);
+            memset(p_desty+ y*p_pic->Y_PITCH + x, p_c->y, p_c->i_num);
+  //          memset(p_desty+ 2*y*p_pic->U_PITCH + x, p_c->cr, p_c->i_num);
+  //          memset(p_desty+ 2*y*p_pic->V_PITCH + x, p_c->cb, p_c->i_num);
+        }
+        j += p_c->i_num;
+        if(j >= p_im->i_cols[i])
+        {
+            i++; j=0;
+        }
+        if( i>= p_im->i_rows) break;
+    }
+    //idem for the second frame
+    p_im = p_r->p_rle_bot; i=0; j=0;
+    for(p_c = p_im->p_codes; p_c->p_next != NULL; p_c=p_c->p_next)
+    {
+        if( p_c->y != 0 && p_c->t < 0x20)
+        {
+            x = j+ p_r->i_x;
+            y = 2*i+1+p_r->i_y;
+//            memset(p_desty+ y*p_pic->U_PITCH*2 + x, p_c->cr, p_c->i_num);
+//            memset(p_desty+ y*p_pic->V_PITCH*2 + x, p_c->cb, p_c->i_num);
+            memset(p_desty+ y*p_pic->Y_PITCH + x, p_c->y, p_c->i_num);
+//            memset(p_desty+ 2*y*p_pic->U_PITCH + x, p_c->cr, p_c->i_num);
+//            memset(p_desty+ 2*y*p_pic->V_PITCH + x, p_c->cb, p_c->i_num);
+        }
+        j += p_c->i_num;
+        if(j >= p_im->i_cols[i])
+        {
+            i++; j=0;
+        }
+        if( i>= p_im->i_rows) break;
+    }
+}
+
+static void dvbsub_Destroy( subpicture_t *p_spu )
+{
+    free_spu( p_spu );
+}
+
+static void dvbsub_render( dvbsub_thread_t *p_dec, dvbsub_all_t* dvbsub)
+{
+    dvbsub_region_t*     p_region;
+    dvbsub_objectdef_t*  p_objectdef;
+    dvbsub_object_t*     p_o;
+    dvbsub_object_t*     p_object;
+    dvbsub_object_t*     p_object_old;
+    dvbsub_render_t*     p_render;
+    dvbsub_rle_t*        p_c;
+    uint8_t i,j;
+    j=0;
+    /* loop on regions */
+    for(i=0; i< dvbsub->p_page->i_regions_number; i++)
+    {
+        p_region = &(dvbsub->p_page->regions[i]);
+    /* loop on objects */
+    for(p_objectdef = p_region->p_object;
+          p_objectdef != NULL;
+            p_objectdef = p_objectdef->p_next)
+    {
+        /* Look for the right object */
+        p_object = dvbsub->p_objects;
+        while((p_object!=NULL) && (p_object->i_id != p_objectdef->i_id))
+        {
+            p_object = p_object->p_next;
+        }
+        if(p_object==NULL)
+        {
+            fprintf(stderr, "Internal DvbSub decoder error\n");
+            return;
+        }
+        /* Allocate the render structure */
+        p_render = trox_malloc(sizeof(dvbsub_render_t));
+        p_render->i_x = p_region->i_x + p_objectdef->i_xoffset;
+        p_render->i_y = p_region->i_y + p_objectdef->i_yoffset;
+        p_render->p_rle_top = p_object->topfield;
+        p_render->p_rle_bot = p_object->bottomfield;
+        /* Compute the color datas according to the appropriate CLUT */
+        for(p_c=p_render->p_rle_top->p_codes;p_c->p_next!=NULL; p_c=p_c->p_next)
+        {
+            //TODO We assume here we are working in 4bp
+            p_c->y=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].Y;
+            p_c->cr=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].Cr;
+            p_c->cb=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].Cb;
+            p_c->t=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].T;
+        }
+        for(p_c=p_render->p_rle_bot->p_codes;p_c->p_next!=NULL; p_c=p_c->p_next)
+        {
+            //TODO We assume here we are working in 4bp
+            p_c->y=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].Y;
+            p_c->cr=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].Cr;
+            p_c->cb=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].Cb;
+            p_c->t=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].T;
+        }
+
+
+        /* Allocate the subpicture internal data. */
+        dvbsub->p_spu[j] = vout_CreateSubPicture( p_dec->p_vout,
+                                                      MEMORY_SUBPICTURE );
+        if( dvbsub->p_spu[j] == NULL )
+        {
+            fprintf(stderr, "Unable to allocate memory ... skipping\n");
+            return;
+        }
+        /* Set the pf_render callback */
+        dvbsub->p_spu[j]->pf_render = dvbsub_RenderDVBSUB;
+        dvbsub->p_spu[j]->p_sys =  trox_malloc( sizeof( subpicture_sys_t ));
+        dvbsub->p_spu[j]->p_sys->p_data = p_render;
+        dvbsub->p_spu[j]->p_sys->b_obsolete=0;
+        dvbsub->p_spu[j]->pf_destroy = dvbsub_Destroy;
+        dvbsub->p_spu[j]->i_start = p_dec->bit_stream.p_pes->i_pts;
+        dvbsub->p_spu[j]->i_stop =  dvbsub->p_spu[j]->i_start + dvbsub->p_page->i_timeout*1000000;
+        dvbsub->p_spu[j]->b_ephemer = VLC_FALSE;
+
+        // At this stage, we have all we need in p_render
+        // We need to free the object
+        //Remove this object from the list
+        p_object_old = p_object;
+        if(p_object == dvbsub->p_objects)
+          dvbsub->p_objects = p_object->p_next;
+        else
+        {
+         for(p_o = dvbsub->p_objects; p_o->p_next != p_object; p_o=p_o->p_next);
+         p_o->p_next = p_object->p_next;
+        }
+        free_object(p_object_old);
+
+        vout_DisplaySubPicture (p_dec->p_vout, dvbsub->p_spu[j] );
+        j++;
+    }
+    }
+}