]> git.sesse.net Git - vlc/blobdiff - modules/demux/ty.c
mkv: Remove an unneeded test.
[vlc] / modules / demux / ty.c
index 22f1d4f80de1ac5e4bd403510604411d8f62eb99..98d48ca831faf6188a136a6cb9e1f94c614c102f 100644 (file)
  * Preamble
  *****************************************************************************/
 
-#include <vlc/vlc.h>
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
 #include <vlc_demux.h>
 #include "vlc_codec.h"
+#include "vlc_meta.h"
+#include "vlc_input.h"
+#include "../codec/cc.h"
+
+#include <assert.h>
 
 /*****************************************************************************
  * Module descriptor
@@ -46,11 +56,11 @@ static int  Open ( vlc_object_t * );
 static void Close( vlc_object_t * );
 
 vlc_module_begin();
-    set_shortname( _("TY") );
-    set_description(_("TY Stream audio/video demux"));
+    set_shortname( N_("TY") );
+    set_description(N_("TY Stream audio/video demux"));
     set_category( CAT_INPUT );
     set_subcategory( SUBCAT_INPUT_DEMUX );
-    set_capability("demux2", 6);
+    set_capability("demux", 6);
     /* FIXME: there seems to be a segfault when using PVR access
      * and TY demux has a bigger priority than PS
      * Something must be wrong.
@@ -109,7 +119,7 @@ typedef struct
   uint8_t ex1, ex2;
   uint8_t rec_type;
   uint8_t subrec_type;
-  vlc_bool_t b_ext;
+  bool b_ext;
   uint64_t l_ty_pts;            /* TY PTS in the record header */
 } ty_rec_hdr_t;
 
@@ -140,17 +150,85 @@ typedef enum
     TIVO_AUDIO_MPEG
 } tivo_audio_t;
 
+#define XDS_MAX_DATA_SIZE (32)
+typedef enum
+{
+    XDS_CLASS_CURRENT        = 0,
+    XDS_CLASS_FUTURE         = 1,
+    XDS_CLASS_CHANNEL        = 2,
+    XDS_CLASS_MISCELLANEOUS  = 3,
+    XDS_CLASS_PUBLIC_SERVICE = 4,
+    XDS_CLASS_RESERVED       = 5,
+    XDS_CLASS_UNDEFINED      = 6,
+    XDS_CLASS_OTHER          = 7,
+
+    XDS_MAX_CLASS_COUNT
+} xds_class_t;
+typedef struct
+{
+    bool b_started;
+    int        i_data;
+    uint8_t    p_data[XDS_MAX_DATA_SIZE];
+    int        i_sum;
+} xds_packet_t;
+typedef enum
+{
+    XDS_META_PROGRAM_RATING_NONE,
+    XDS_META_PROGRAM_RATING_MPAA,
+    XDS_META_PROGRAM_RATING_TPG,
+    /* TODO add CA/CE rating */
+} xds_meta_program_rating_t;
+typedef struct
+{
+    char *psz_name;
+    xds_meta_program_rating_t rating;
+    char *psz_rating;
+    /* Add the other fields once I have the samples */
+} xds_meta_program_t;
+typedef struct
+{
+    char *psz_channel_name;
+    char *psz_channel_call_letter;
+    char *psz_channel_number;
+
+    xds_meta_program_t  current;
+    xds_meta_program_t  future;
+} xds_meta_t;
+typedef struct
+{
+    /* Are we in XDS mode */
+    bool b_xds;
+
+    /* Current class type */
+    xds_class_t i_class;
+    int         i_type;
+    bool  b_future;
+
+    /* */
+    xds_packet_t pkt[XDS_MAX_CLASS_COUNT][128]; /* XXX it is way too much, but simpler */
+
+    /* */
+    bool  b_meta_changed;
+    xds_meta_t  meta;
+
+} xds_t;
+
 struct demux_sys_t
 {
   es_out_id_t *p_video;               /* ptr to video codec */
   es_out_id_t *p_audio;               /* holds either ac3 or mpeg codec ptr */
 
+  cc_data_t   cc;
+  es_out_id_t *p_cc[4];
+
+  xds_t       xds;
+
   int             i_cur_chunk;
   int             i_stuff_cnt;
   size_t          i_stream_size;      /* size of input stream (if known) */
   //uint64_t        l_program_len;      /* length of this stream in msec */
-  vlc_bool_t      b_seekable;         /* is this stream seekable? */
-  vlc_bool_t      b_have_master;      /* are master chunks present? */
+  bool      b_seekable;         /* is this stream seekable? */
+  bool      b_have_master;      /* are master chunks present? */
   tivo_type_t     tivo_type;          /* tivo type (SA / DTiVo) */
   tivo_series_t   tivo_series;        /* Series1 or Series2 */
   tivo_audio_t    audio_type;         /* AC3 or MPEG */
@@ -175,8 +253,8 @@ struct demux_sys_t
   int             i_num_recs;         /* number of recs in this chunk */
   int             i_seq_rec;          /* record number where seq start is */
   ty_seq_table_t  *seq_table;         /* table of SEQ entries from mstr chk */
-  vlc_bool_t      eof;
-  vlc_bool_t      b_first_chunk;
+  bool      eof;
+  bool      b_first_chunk;
 };
 
 static int get_chunk_header(demux_t *);
@@ -192,6 +270,17 @@ static int probe_stream(demux_t *p_demux);
 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk);
 static void parse_master(demux_t *p_demux);
 
+static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
+static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
+static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );
+
+static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 );
+
+static void XdsInit( xds_t * );
+static void XdsExit( xds_t * );
+
+#define TY_ES_GROUP (1)
+
 /*
  * Open: check file and initialize demux structures
  *
@@ -208,6 +297,7 @@ static int Open(vlc_object_t *p_this)
     demux_sys_t *p_sys;
     es_format_t fmt;
     const uint8_t *p_peek;
+    int i;
 
     /* peek at the first 12 bytes. */
     /* for TY streams, they're always the same */
@@ -218,7 +308,9 @@ static int Open(vlc_object_t *p_this)
          U32_AT(&p_peek[4]) != 0x02 ||
          U32_AT(&p_peek[8]) != CHUNK_SIZE )
     {
-        if( !p_demux->b_force )
+        if( !p_demux->b_force &&
+            !demux_IsPathExtension( p_demux, ".ty" ) &&
+            !demux_IsPathExtension( p_demux, ".ty+" ) )
             return VLC_EGENERIC;
         msg_Warn( p_demux, "this does not look like a TY file, "
                            "continuing anyway..." );
@@ -236,7 +328,7 @@ static int Open(vlc_object_t *p_this)
     memset(p_sys, 0, sizeof(demux_sys_t));
 
     /* set up our struct (most were zero'd out with the memset above) */
-    p_sys->b_first_chunk = VLC_TRUE;
+    p_sys->b_first_chunk = true;
     p_sys->b_have_master = (U32_AT(p_peek) == TIVO_PES_FILEID);
     p_sys->firstAudioPTS = -1;
     p_sys->i_stream_size = stream_Size(p_demux->s);
@@ -264,21 +356,202 @@ static int Open(vlc_object_t *p_this)
     } else {
         es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', ' ' ) );
     }
+    fmt.i_group = TY_ES_GROUP;
     p_sys->p_audio = es_out_Add( p_demux->out, &fmt );
 
     /* register the video stream */
     es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( 'm', 'p', 'g', 'v' ) );
+    fmt.i_group = TY_ES_GROUP;
     p_sys->p_video = es_out_Add( p_demux->out, &fmt );
 
-#if 0
-    /* register the CC decoder */
-    es_format_Init( &fmt, SPU_ES, VLC_FOURCC('s', 'u', 'b', 't'));
-    p_sys->p_subt_es = es_out_Add(p_demux->out, &fmt);
-#endif
+    /* */
+    for( i = 0; i < 4; i++ )
+        p_sys->p_cc[i] = NULL;
+    cc_Init( &p_sys->cc );
+
+    XdsInit( &p_sys->xds );
 
     return VLC_SUCCESS;
 }
 
+/* =========================================================================== */
+/* Demux: Read & Demux one record from the chunk
+ *
+ * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
+ *
+ * NOTE: I think we can return the number of packets sent instead of just 1.
+ * that means we can demux an entire chunk and shoot it back (may be more efficient)
+ * -- should try that some day :) --
+ */
+static int Demux( demux_t *p_demux )
+{
+    demux_sys_t  *p_sys = p_demux->p_sys;
+    ty_rec_hdr_t *p_rec;
+    block_t      *p_block_in = NULL;
+
+    /*msg_Dbg(p_demux, "ty demux processing" );*/
+   
+    /* did we hit EOF earlier? */
+    if( p_sys->eof )
+        return 0;
+
+    /*
+     * what we do (1 record now.. maybe more later):
+    * - use stream_Read() to read the chunk header & record headers
+    * - discard entire chunk if it is a PART header chunk
+    * - parse all the headers into record header array
+    * - keep a pointer of which record we're on
+    * - use stream_Block() to fetch each record
+    * - parse out PTS from PES headers
+    * - set PTS for data packets
+    * - pass the data on to the proper codec via es_out_Send()
+
+    * if this is the first time or  
+    * if we're at the end of this chunk, start a new one
+    */
+    /* parse the next chunk's record headers */
+    if( p_sys->b_first_chunk || p_sys->i_cur_rec >= p_sys->i_num_recs )
+    {
+        if( get_chunk_header(p_demux) == 0 )
+            return 0;
+    }
+
+    /*======================================================================
+     * parse & send one record of the chunk
+     *====================================================================== */
+    p_rec = &p_sys->rec_hdrs[p_sys->i_cur_rec];
+
+    if( !p_rec->b_ext )
+    {
+        const long l_rec_size = p_rec->l_rec_size;
+        /*msg_Dbg(p_demux, "Record Type 0x%x/%02x %ld bytes",
+                    subrec_type, p_rec->rec_type, l_rec_size );*/
+  
+        /* some normal records are 0 length, so check for that... */
+        if( l_rec_size <= 0 )
+        {
+            /* no data in payload; we're done */
+            p_sys->i_cur_rec++;
+            return 1;
+        }
+
+        /* read in this record's payload */
+        if( !( p_block_in = stream_Block( p_demux->s, l_rec_size ) ) )
+            return 0;
+
+        /* set these as 'unknown' for now */
+        p_block_in->i_pts =
+        p_block_in->i_dts = 0;
+    }
+    /*else
+    {
+        -- don't read any data from the stream, data was in the record header --
+        msg_Dbg(p_demux,
+               "Record Type 0x%02x/%02x, ext data = %02x, %02x", subrec_type,
+                p_rec->rec_type, p_rec->ex1, p_rec->ex2);
+    }*/
+
+    if( p_rec->rec_type == 0xe0 )
+    {
+        /* Video */
+        DemuxRecVideo( p_demux, p_rec, p_block_in );
+    }
+    else if ( p_rec->rec_type == 0xc0 )
+    {
+        /* Audio */
+        DemuxRecAudio( p_demux, p_rec, p_block_in );
+    }
+    else if( p_rec->rec_type == 0x01 || p_rec->rec_type == 0x02 )
+    {
+        /* Closed Captions/XDS */
+        DemuxRecCc( p_demux, p_rec, p_block_in );
+    }
+    else if ( p_rec->rec_type == 0x03 )
+    {
+        /* Tivo data services (e.g. "thumbs-up to record!")  useless for us */
+        if( p_block_in )
+            block_Release(p_block_in);
+    }
+    else if ( p_rec->rec_type == 0x05 )
+    {
+        /* Unknown, but seen regularly */
+        if( p_block_in )
+            block_Release(p_block_in);
+    }
+    else
+    {
+        msg_Dbg(p_demux, "Invalid record type 0x%02x", p_rec->rec_type );
+        if( p_block_in )
+            block_Release(p_block_in);
+    }
+
+    /* */
+    p_sys->i_cur_rec++;
+    return 1;
+}
+
+/* Control */
+static int Control(demux_t *p_demux, int i_query, va_list args)
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    double f, *pf;
+    int64_t i64, *p_i64;
+
+    /*msg_Info(p_demux, "control cmd %d", i_query);*/
+    switch( i_query )
+    {
+    case DEMUX_GET_POSITION:
+        /* arg is 0.0 - 1.0 percent of overall file position */
+        if( ( i64 = p_sys->i_stream_size ) > 0 )
+        {
+            pf = (double*) va_arg( args, double* );
+            *pf = (double)stream_Tell( p_demux->s ) / (double) i64;
+            return VLC_SUCCESS;
+        }
+        return VLC_EGENERIC;
+
+    case DEMUX_SET_POSITION:
+        /* arg is 0.0 - 1.0 percent of overall file position */
+        f = (double) va_arg( args, double );
+        /* msg_Dbg(p_demux, "Control - set position to %2.3f", f); */
+        if ((i64 = p_sys->i_stream_size) > 0)
+            return ty_stream_seek_pct(p_demux, f);
+        return VLC_EGENERIC;
+    case DEMUX_GET_TIME:
+        /* return TiVo timestamp */
+        p_i64 = (int64_t *) va_arg(args, int64_t *);
+        //*p_i64 = p_sys->lastAudioPTS - p_sys->firstAudioPTS;
+        //*p_i64 = (p_sys->l_last_ty_pts / 1000) + (p_sys->lastAudioPTS -
+        //    p_sys->l_last_ty_pts_sync);
+        *p_i64 = (p_sys->l_last_ty_pts / 1000);
+        return VLC_SUCCESS;
+    case DEMUX_GET_LENGTH:    /* length of program in microseconds, 0 if unk */
+        /* size / bitrate */
+        p_i64 = (int64_t *) va_arg(args, int64_t *);
+        *p_i64 = 0;
+        return VLC_SUCCESS;
+    case DEMUX_SET_TIME:      /* arg is time in microsecs */
+        i64 = (int64_t) va_arg( args, int64_t );
+        return ty_stream_seek_time(p_demux, i64 * 1000);
+    case DEMUX_GET_FPS:
+    default:
+        return VLC_EGENERIC;
+    }
+}
+
+/* Close */
+static void Close( vlc_object_t *p_this )
+{
+    demux_t *p_demux = (demux_t*)p_this;
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    XdsExit( &p_sys->xds );
+    cc_Exit( &p_sys->cc );
+    free( p_sys->rec_hdrs );
+    free( p_sys->seq_table );
+    free(p_sys);
+}
+
 
 /* =========================================================================== */
 /* Compute Presentation Time Stamp (PTS)
@@ -381,207 +654,178 @@ static int check_sync_pes( demux_t *p_demux, block_t *p_block,
     return 0;
 }
 
-
-/* =========================================================================== */
-/* Demux: Read & Demux one record from the chunk
- *
- * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
- *
- * NOTE: I think we can return the number of packets sent instead of just 1.
- * that means we can demux an entire chunk and shoot it back (may be more efficient)
- * -- should try that some day :) --
- */
-static int Demux( demux_t *p_demux )
+static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
 {
-    demux_sys_t      *p_sys = p_demux->p_sys;
-
-    int              invalidType = 0;
-    int              recordsDecoded = 0;
-
-    int              rec_type;
-    long             l_rec_size;
-    int              i_cur_rec;
-    int              subrec_type;
-    ty_rec_hdr_t     *rec_hdr;
-
-    block_t          *p_block_in = NULL;
-    int              esOffset1;
-
-    uint8_t          lastCC[ 16 ];
-    uint8_t          lastXDS[ 16 ];
-
-    /*msg_Dbg(p_demux, "ty demux processing" );*/
-   
-    /* did we hit EOF earlier? */
-    if (p_sys->eof) return 0;
-
-    /*
-     * what we do (1 record now.. maybe more later):
-    * - use stream_Read() to read the chunk header & record headers
-    * - discard entire chunk if it is a PART header chunk
-    * - parse all the headers into record header array
-    * - keep a pointer of which record we're on
-    * - use stream_Block() to fetch each record
-    * - parse out PTS from PES headers
-    * - set PTS for data packets
-    * - pass the data on to the proper codec via es_out_Send()
+    demux_sys_t *p_sys = p_demux->p_sys;
+    const int subrec_type = rec_hdr->subrec_type;
+    const long l_rec_size = rec_hdr->l_rec_size;    // p_block_in->i_buffer might be better
+    int esOffset1;
+    int i;
 
-    * if this is the first time or  
-    * if we're at the end of this chunk, start a new one
-    */
-    /* parse the next chunk's record headers */
-    if (p_sys->b_first_chunk || p_sys->i_cur_rec >= p_sys->i_num_recs)
-        if (get_chunk_header(p_demux) == 0)
-            return 0;
+    assert( rec_hdr->rec_type == 0xe0 );
+    if( !p_block_in )
+        return -1;
 
-    /*======================================================================
-     * parse & send one record of the chunk
-     *====================================================================== */
-    i_cur_rec = p_sys->i_cur_rec;
-    recordsDecoded++;
-    rec_hdr = &p_sys->rec_hdrs[ i_cur_rec ];
-    subrec_type = rec_hdr->subrec_type;
-    rec_type = rec_hdr->rec_type;
-    l_rec_size = rec_hdr->l_rec_size;
-
-    if (!rec_hdr->b_ext)
+#if 0
+    msg_Dbg(p_demux, "packet buffer has "
+            "%02x %02x %02x %02x %02x %02x %02x %02x "
+            "%02x %02x %02x %02x %02x %02x %02x %02x",
+            p_block_in->p_buffer[0], p_block_in->p_buffer[1],
+            p_block_in->p_buffer[2], p_block_in->p_buffer[3],
+            p_block_in->p_buffer[4], p_block_in->p_buffer[5],
+            p_block_in->p_buffer[6], p_block_in->p_buffer[7],
+            p_block_in->p_buffer[8], p_block_in->p_buffer[9],
+            p_block_in->p_buffer[10], p_block_in->p_buffer[11],
+            p_block_in->p_buffer[12], p_block_in->p_buffer[13],
+            p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
+#endif
+    //if( subrec_type == 0x06 || subrec_type == 0x07 )
+    if( subrec_type != 0x02 && subrec_type != 0x0c &&
+        subrec_type != 0x08 && l_rec_size > 4 )
     {
-        /*msg_Dbg(p_demux, "Record Type 0x%x/%02x %ld bytes",
-                    subrec_type, rec_type, l_rec_size );*/
-  
-        /* some normal records are 0 length, so check for that... */
-        if (l_rec_size > 0)
+        /* get the PTS from this packet if it has one.
+         * on S1, only 0x06 has PES.  On S2, however, most all do.
+         * Do NOT Pass the PES Header to the MPEG2 codec */
+        esOffset1 = find_es_header( ty_VideoPacket, p_block_in->p_buffer, 5 );
+        if( esOffset1 != -1 )
         {
-            /* read in this record's payload */
-            if( !( p_block_in = stream_Block( p_demux->s, l_rec_size ) ) )
-            {
-                /* EOF */
-                p_sys->eof = 1;
-                return 0;
+            //msg_Dbg(p_demux, "Video PES hdr in pkt type 0x%02x at offset %d",
+                //subrec_type, esOffset1);
+            p_sys->lastVideoPTS = get_pts(
+                    &p_block_in->p_buffer[ esOffset1 + VIDEO_PTS_OFFSET ] );
+            /*msg_Dbg(p_demux, "Video rec %d PTS %"PRId64, p_sys->i_cur_rec,
+                        p_sys->lastVideoPTS );*/
+            if (subrec_type != 0x06) {
+                /* if we found a PES, and it's not type 6, then we're S2 */
+                /* The packet will have video data (& other headers) so we
+                 * chop out the PES header and send the rest */
+                if (l_rec_size >= VIDEO_PES_LENGTH) {
+                    p_block_in->p_buffer += VIDEO_PES_LENGTH + esOffset1;
+                    p_block_in->i_buffer -= VIDEO_PES_LENGTH + esOffset1;
+                } else {
+                    msg_Dbg(p_demux, "video rec type 0x%02x has short PES"
+                        " (%ld bytes)", subrec_type, l_rec_size);
+                    /* nuke this block; it's too short, but has PES marker */
+                    p_block_in->i_buffer = 0;
+                }
             }
-            /* set these as 'unknown' for now */
-            p_block_in->i_pts = p_block_in->i_dts = 0;
+        }/* else
+            msg_Dbg(p_demux, "No Video PES hdr in pkt type 0x%02x",
+                subrec_type); */
+    }
+
+    if(subrec_type == 0x06 )
+    {
+        /* type 6 (S1 DTivo) has no data, so we're done */
+        block_Release(p_block_in);
+        return 0;
+    }
+
+    /* if it's not a continue blk, then set PTS */
+    if( subrec_type != 0x02 )
+    {
+        /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
+                   subrec_type);*/
+        /* if it's a GOP header, make sure it's legal
+         * (if we have enough data) */
+        /* Some ty files don't have this bit set
+         * and it causes problems */
+        if (subrec_type == 0x0c && l_rec_size >= 6)
+            p_block_in->p_buffer[5] |= 0x08;
+        /* store the TY PTS if there is one */
+        if (subrec_type == 0x07) {
+            p_sys->l_last_ty_pts = rec_hdr->l_ty_pts;
+            /* should we use audio or video PTS? */
+            //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
+        } else {
+            /* yes I know this is a cheap hack.  It's the timestamp
+               used for display and skipping fwd/back, so it
+               doesn't have to be accurate to the millisecond.
+               I adjust it here by roughly one 1/30 sec.  Yes it
+               will be slightly off for UK streams, but it's OK.
+             */
+            p_sys->l_last_ty_pts += 35000000;
+            //p_sys->l_last_ty_pts += 33366667;
         }
-        else
+        /* set PTS for this block before we send */
+        if (p_sys->lastVideoPTS > 0)
         {
-            /* no data in payload; we're done */
-            p_sys->i_cur_rec++;
-            return 1;
+            p_block_in->i_pts = p_sys->lastVideoPTS;
+            /* PTS gets used ONCE. 
+             * Any subsequent frames we get BEFORE next PES
+             * header will have their PTS computed in the codec */
+            p_sys->lastVideoPTS = 0;
         }
     }
-    /*else
+
+    /* Register the CC decoders when needed */
+    for( i = 0; i < 4; i++ )
     {
-        -- don't read any data from the stream, data was in the record header --
-        msg_Dbg(p_demux,
-               "Record Type 0x%02x/%02x, ext data = %02x, %02x", subrec_type,
-                rec_type, rec_hdr->ex1, rec_hdr->ex2);
-    }*/
+        static const vlc_fourcc_t fcc[4] = {
+            VLC_FOURCC('c', 'c', '1', ' '),
+            VLC_FOURCC('c', 'c', '2', ' '),
+            VLC_FOURCC('c', 'c', '3', ' '),
+            VLC_FOURCC('c', 'c', '4', ' ')
+        };
+        static const char *ppsz_description[4] = {
+            N_("Closed captions 1"),
+            N_("Closed captions 2"),
+            N_("Closed captions 3"),
+            N_("Closed captions 4"),
+        };
+
+        es_format_t fmt;
+
+        if( !p_sys->cc.pb_present[i] || p_sys->p_cc[i] )
+            continue;
+
+        es_format_Init( &fmt, SPU_ES, fcc[i] );
+        fmt.psz_description = strdup( _(ppsz_description[i]) );
+        fmt.i_group = TY_ES_GROUP;
+        p_sys->p_cc[i] = es_out_Add( p_demux->out, &fmt );
+        es_format_Clean( &fmt );
 
-    /*================================================================*
-     * Video Parsing
-     *================================================================*/
-    if ( rec_type == 0xe0 )
+    }
+    /* Send the CC data */
+    if( p_block_in->i_pts > 0 && p_sys->cc.i_data > 0 )
     {
-#if 0
-        msg_Dbg(p_demux, "packet buffer has "
-                "%02x %02x %02x %02x %02x %02x %02x %02x "
-                "%02x %02x %02x %02x %02x %02x %02x %02x",
-                p_block_in->p_buffer[0], p_block_in->p_buffer[1],
-                p_block_in->p_buffer[2], p_block_in->p_buffer[3],
-                p_block_in->p_buffer[4], p_block_in->p_buffer[5],
-                p_block_in->p_buffer[6], p_block_in->p_buffer[7],
-                p_block_in->p_buffer[8], p_block_in->p_buffer[9],
-                p_block_in->p_buffer[10], p_block_in->p_buffer[11],
-                p_block_in->p_buffer[12], p_block_in->p_buffer[13],
-                p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
-#endif
-        //if( subrec_type == 0x06 || subrec_type == 0x07 )
-        if( subrec_type != 0x02 && subrec_type != 0x0c
-            && subrec_type != 0x08 && l_rec_size > 4 )
+        int i_cc_count;
+
+        block_t *p_cc = block_New( p_demux, p_sys->cc.i_data );
+        p_cc->i_flags |= BLOCK_FLAG_TYPE_I;
+        p_cc->i_pts = p_block_in->i_pts;
+        memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
+
+        for( i = 0, i_cc_count = 0; i < 4; i++ )
+            i_cc_count += p_sys->p_cc[i] ? 1 : 0;
+
+        for( i = 0; i < 4; i++ )
         {
-            /* get the PTS from this packet if it has one.
-             * on S1, only 0x06 has PES.  On S2, however, most all do.
-             * Do NOT Pass the PES Header to the MPEG2 codec */
-            esOffset1 = find_es_header( ty_VideoPacket, p_block_in->p_buffer,
-                    5 );
-            if ( esOffset1 != -1 )
-            {
-                //msg_Dbg(p_demux, "Video PES hdr in pkt type 0x%02x at offset %d",
-                    //subrec_type, esOffset1);
-                p_sys->lastVideoPTS = get_pts(
-                        &p_block_in->p_buffer[ esOffset1 + VIDEO_PTS_OFFSET ] );
-                /*msg_Dbg(p_demux, "Video rec %d PTS "I64Fd, p_sys->i_cur_rec,
-                            p_sys->lastVideoPTS );*/
-                if (subrec_type != 0x06) {
-                    /* if we found a PES, and it's not type 6, then we're S2 */
-                    /* The packet will have video data (& other headers) so we
-                     * chop out the PES header and send the rest */
-                    if (l_rec_size >= VIDEO_PES_LENGTH) {
-                        p_block_in->p_buffer += VIDEO_PES_LENGTH + esOffset1;
-                        p_block_in->i_buffer -= VIDEO_PES_LENGTH + esOffset1;
-                    } else {
-                        msg_Dbg(p_demux, "video rec type 0x%02x has short PES"
-                            " (%ld bytes)", subrec_type, l_rec_size);
-                        /* nuke this block; it's too short, but has PES marker */
-                        p_block_in->i_buffer = 0;
-                    }
-                }
-            }/* else
-                msg_Dbg(p_demux, "No Video PES hdr in pkt type 0x%02x",
-                    subrec_type); */
-        }
-        if (subrec_type == 0x06) {
-            /* type 6 (S1 DTivo) has no data, so we're done */
-            block_Release(p_block_in);
-        } else {
-            /* if it's not a continue blk, then set PTS */
-            if (subrec_type != 0x02)
-            {
-                /*msg_Dbg(p_demux, "Video rec %d type 0x%02X", p_sys->i_cur_rec,
-                           subrec_type);*/
-                /* if it's a GOP header, make sure it's legal
-                 * (if we have enough data) */
-                /* Some ty files don't have this bit set
-                 * and it causes problems */
-                if (subrec_type == 0x0c && l_rec_size >= 6)
-                    p_block_in->p_buffer[5] |= 0x08;
-                /* store the TY PTS if there is one */
-                if (subrec_type == 0x07) {
-                    p_sys->l_last_ty_pts = rec_hdr->l_ty_pts;
-                    /* should we use audio or video PTS? */
-                    //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS;
-                } else {
-                    /* yes I know this is a cheap hack.  It's the timestamp
-                       used for display and skipping fwd/back, so it
-                       doesn't have to be accurate to the millisecond.
-                       I adjust it here by roughly one 1/30 sec.  Yes it
-                       will be slightly off for UK streams, but it's OK.
-                     */
-                    p_sys->l_last_ty_pts += 35000000;
-                    //p_sys->l_last_ty_pts += 33366667;
-                }
-                /* set PTS for this block before we send */
-                if (p_sys->lastVideoPTS > 0)
-                {
-                    p_block_in->i_pts = p_sys->lastVideoPTS;
-                    /* PTS gets used ONCE. 
-                     * Any subsequent frames we get BEFORE next PES
-                     * header will have their PTS computed in the codec */
-                    p_sys->lastVideoPTS = 0;
-                }
-            }
-            //msg_Dbg(p_demux, "sending rec %d as video type 0x%02x",
-                    //p_sys->i_cur_rec, subrec_type);
-            es_out_Send(p_demux->out, p_sys->p_video, p_block_in);
+            if( !p_sys->p_cc[i] )
+                continue;
+            if( i_cc_count > 1 )
+                es_out_Send( p_demux->out, p_sys->p_cc[i], block_Duplicate( p_cc ) );
+            else
+                es_out_Send( p_demux->out, p_sys->p_cc[i], p_cc );
         }
-    } /* end if video rec type */
-    /* ================================================================
-     * Audio Parsing
-     * ================================================================
-     * parse PES headers and send the rest to the codec
-     */
-    else if ( rec_type == 0xc0 )
-    {
+        cc_Flush( &p_sys->cc );
+    }
+
+    //msg_Dbg(p_demux, "sending rec %d as video type 0x%02x",
+            //p_sys->i_cur_rec, subrec_type);
+    es_out_Send(p_demux->out, p_sys->p_video, p_block_in);
+    return 0;
+}
+static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    const int subrec_type = rec_hdr->subrec_type;
+    const long l_rec_size = rec_hdr->l_rec_size;
+    int esOffset1;
+
+    assert( rec_hdr->rec_type == 0xc0 );
+    if( !p_block_in )
+        return -1;
 #if 0
         int i;
         printf( "Audio Packet Header " );
@@ -590,263 +834,224 @@ static int Demux( demux_t *p_demux )
         printf( "\n" );
 #endif
 
-        /* SA or DTiVo Audio Data, no PES (continued block)
-         * ================================================
-         */
-        if ( subrec_type == 2 )
-        {
-            /* continue PES if previous was incomplete */
-            if (p_sys->i_pes_buf_cnt > 0)
-            {
-                int i_need = p_sys->i_Pes_Length - p_sys->i_pes_buf_cnt;
-
-                msg_Dbg(p_demux, "continuing PES header");
-                /* do we have enough data to complete? */
-                if (i_need < l_rec_size)
-                {
-                    /* we have enough; reconstruct this p_frame with the new hdr */
-                    memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
-                           p_block_in->p_buffer, i_need);
-                    /* advance the block past the PES header (don't want to send it) */
-                    p_block_in->p_buffer += i_need;
-                    p_block_in->i_buffer -= i_need;
-                    /* get the PTS out of this PES header (MPEG or AC3) */
-                    if (p_sys->audio_type == TIVO_AUDIO_MPEG)
-                        esOffset1 = find_es_header(ty_MPEGAudioPacket,
-                                p_sys->pes_buffer, 5);
-                    else
-                        esOffset1 = find_es_header(ty_AC3AudioPacket,
-                                p_sys->pes_buffer, 5);
-                    if (esOffset1 < 0)
-                    {
-                        /* god help us; something's really wrong */
-                        msg_Err(p_demux, "can't find audio PES header in packet");
-                    }
-                    else
-                    {
-                        p_sys->lastAudioPTS = get_pts( 
-                            &p_sys->pes_buffer[ esOffset1 + p_sys->i_Pts_Offset ] );
-                        p_block_in->i_pts = p_sys->lastAudioPTS;
-                    }
-                    p_sys->i_pes_buf_cnt = 0;
-                }
-                else
-                {
-                    /* don't have complete PES hdr; save what we have and return */
-                    memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
-                            p_block_in->p_buffer, l_rec_size);
-                    p_sys->i_pes_buf_cnt += l_rec_size;
-                    p_sys->i_cur_rec++;
-                    block_Release(p_block_in);
-                    return 1;
-                }
-            }
-            /* S2 DTivo has AC3 packets with 2 padding bytes at end.  This is
-             * not allowed in the AC3 spec and will cause problems.  So here
-             * we try to trim things. */
-            /* Also, S1 DTivo has alternating short / long AC3 packets.  That
-             * is, one packet is short (incomplete) and the next packet has
-             * the first one's missing data, plus all of its own.  Strange. */
-            if (p_sys->audio_type == TIVO_AUDIO_AC3 &&
-                    p_sys->tivo_series == TIVO_SERIES2) {
-                if (p_sys->l_ac3_pkt_size + p_block_in->i_buffer >
-                        AC3_PKT_LENGTH) {
-                    p_block_in->i_buffer -= 2;
-                    p_sys->l_ac3_pkt_size = 0;
-                } else {
-                    p_sys->l_ac3_pkt_size += p_block_in->i_buffer;
-                }
-            }
-            es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
-        } /* subrec == 2 */
+    if( subrec_type == 2 )
+    {
+        /* SA or DTiVo Audio Data, no PES (continued block)
+         * ================================================
+         */
 
-        /* MPEG Audio with PES Header, either SA or DTiVo   */
-        /* ================================================ */
-        if ( subrec_type == 0x03 )
+        /* continue PES if previous was incomplete */
+        if (p_sys->i_pes_buf_cnt > 0)
         {
-            esOffset1 = find_es_header( ty_MPEGAudioPacket,
-                    p_block_in->p_buffer, 5 );
+            const int i_need = p_sys->i_Pes_Length - p_sys->i_pes_buf_cnt;
 
-            /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
-               p_block_in->p_buffer[0], p_block_in->p_buffer[1],
-               p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
-            msg_Dbg(p_demux, "audio ES hdr at offset %d", esOffset1);*/
-
-            /* SA PES Header, No Audio Data                     */
-            /* ================================================ */
-            if ( ( esOffset1 == 0 ) && ( l_rec_size == 16 ) )
+            msg_Dbg(p_demux, "continuing PES header");
+            /* do we have enough data to complete? */
+            if (i_need >= l_rec_size)
             {
-                p_sys->lastAudioPTS = get_pts( &p_block_in->p_buffer[
-                            SA_PTS_OFFSET ] );
-                if (p_sys->firstAudioPTS < 0)
-                    p_sys->firstAudioPTS = p_sys->lastAudioPTS;
+                /* don't have complete PES hdr; save what we have and return */
+                memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
+                        p_block_in->p_buffer, l_rec_size);
+                p_sys->i_pes_buf_cnt += l_rec_size;
+                /* */
                 block_Release(p_block_in);
-                /*msg_Dbg(p_demux, "SA Audio PTS %lld",
-                           p_sys->lastAudioPTS );*/
+                return 0;
             }
+
+            /* we have enough; reconstruct this p_frame with the new hdr */
+            memcpy(&p_sys->pes_buffer[p_sys->i_pes_buf_cnt],
+                   p_block_in->p_buffer, i_need);
+            /* advance the block past the PES header (don't want to send it) */
+            p_block_in->p_buffer += i_need;
+            p_block_in->i_buffer -= i_need;
+            /* get the PTS out of this PES header (MPEG or AC3) */
+            if (p_sys->audio_type == TIVO_AUDIO_MPEG)
+                esOffset1 = find_es_header(ty_MPEGAudioPacket,
+                        p_sys->pes_buffer, 5);
             else
-            /* DTiVo Audio with PES Header                      */
-            /* ================================================ */
+                esOffset1 = find_es_header(ty_AC3AudioPacket,
+                        p_sys->pes_buffer, 5);
+            if (esOffset1 < 0)
             {
-                /* Check for complete PES */
-                if (check_sync_pes(p_demux, p_block_in, esOffset1,
-                                    l_rec_size) == -1)
-                {
-                    /* partial PES header found, nothing else. 
-                     * we're done. */
-                    p_sys->i_cur_rec++;
-                    block_Release(p_block_in);
-                    return 1;
-                }
-#if 0
-                msg_Dbg(p_demux, "packet buffer has "
-                         "%02x %02x %02x %02x %02x %02x %02x %02x "
-                         "%02x %02x %02x %02x %02x %02x %02x %02x",
-                         p_block_in->p_buffer[0], p_block_in->p_buffer[1],
-                         p_block_in->p_buffer[2], p_block_in->p_buffer[3],
-                         p_block_in->p_buffer[4], p_block_in->p_buffer[5],
-                         p_block_in->p_buffer[6], p_block_in->p_buffer[7],
-                         p_block_in->p_buffer[8], p_block_in->p_buffer[9],
-                         p_block_in->p_buffer[10], p_block_in->p_buffer[11],
-                         p_block_in->p_buffer[12], p_block_in->p_buffer[13],
-                         p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
-#endif
-                /* set PCR before we send */
-                if( p_block_in->i_pts > 0 )
-                    es_out_Control( p_demux->out, ES_OUT_SET_PCR,
-                                    p_block_in->i_pts );
-                es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
-            }   /* if DTiVo */
-        }   /* if subrec == 0x03 */
-
-        /* SA Audio with no PES Header                      */
-        /* ================================================ */
-        if ( subrec_type == 0x04 )
-        {
-            /*msg_Dbg(p_demux,
-                    "Adding SA Audio Packet Size %ld", l_rec_size ); */
-
-            /* set PCR before we send */
-            if (p_sys->lastAudioPTS > 0)
+                /* god help us; something's really wrong */
+                msg_Err(p_demux, "can't find audio PES header in packet");
+            }
+            else
             {
+                p_sys->lastAudioPTS = get_pts( 
+                    &p_sys->pes_buffer[ esOffset1 + p_sys->i_Pts_Offset ] );
                 p_block_in->i_pts = p_sys->lastAudioPTS;
-                es_out_Control( p_demux->out, ES_OUT_SET_PCR,
-                                p_block_in->i_pts );
             }
-            es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
+            p_sys->i_pes_buf_cnt = 0;
+        }
+        /* S2 DTivo has AC3 packets with 2 padding bytes at end.  This is
+         * not allowed in the AC3 spec and will cause problems.  So here
+         * we try to trim things. */
+        /* Also, S1 DTivo has alternating short / long AC3 packets.  That
+         * is, one packet is short (incomplete) and the next packet has
+         * the first one's missing data, plus all of its own.  Strange. */
+        if (p_sys->audio_type == TIVO_AUDIO_AC3 &&
+                p_sys->tivo_series == TIVO_SERIES2) {
+            if (p_sys->l_ac3_pkt_size + p_block_in->i_buffer >
+                    AC3_PKT_LENGTH) {
+                p_block_in->i_buffer -= 2;
+                p_sys->l_ac3_pkt_size = 0;
+            } else {
+                p_sys->l_ac3_pkt_size += p_block_in->i_buffer;
+            }
         }
+    }
+    else if( subrec_type == 0x03 )
+    {
+        /* MPEG Audio with PES Header, either SA or DTiVo   */
+        /* ================================================ */
+        esOffset1 = find_es_header( ty_MPEGAudioPacket,
+                p_block_in->p_buffer, 5 );
 
-        /* DTiVo AC3 Audio Data with PES Header             */
+        /*msg_Dbg(p_demux, "buffer has %#02x %#02x %#02x %#02x",
+           p_block_in->p_buffer[0], p_block_in->p_buffer[1],
+           p_block_in->p_buffer[2], p_block_in->p_buffer[3]);
+        msg_Dbg(p_demux, "audio ES hdr at offset %d", esOffset1);*/
+
+        /* SA PES Header, No Audio Data                     */
         /* ================================================ */
-        if ( subrec_type == 0x09 )
+        if ( ( esOffset1 == 0 ) && ( l_rec_size == 16 ) )
         {
-            esOffset1 = find_es_header( ty_AC3AudioPacket,
-                    p_block_in->p_buffer, 5 );
+            p_sys->lastAudioPTS = get_pts( &p_block_in->p_buffer[
+                        SA_PTS_OFFSET ] );
+            if (p_sys->firstAudioPTS < 0)
+                p_sys->firstAudioPTS = p_sys->lastAudioPTS;
 
-#if 0
-            msg_Dbg(p_demux, "buffer has "
-                     "%02x %02x %02x %02x %02x %02x %02x %02x "
-                     "%02x %02x %02x %02x %02x %02x %02x %02x",
-                     p_block_in->p_buffer[0], p_block_in->p_buffer[1],
-                     p_block_in->p_buffer[2], p_block_in->p_buffer[3],
-                     p_block_in->p_buffer[4], p_block_in->p_buffer[5],
-                     p_block_in->p_buffer[6], p_block_in->p_buffer[7],
-                     p_block_in->p_buffer[8], p_block_in->p_buffer[9],
-                     p_block_in->p_buffer[10], p_block_in->p_buffer[11],
-                     p_block_in->p_buffer[12], p_block_in->p_buffer[13],
-                     p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
-            msg_Dbg(p_demux, "audio ES AC3 hdr at offset %d", esOffset1);
-#endif
+            block_Release(p_block_in);
+            return 0;
+            /*msg_Dbg(p_demux, "SA Audio PTS %lld",
+                       p_sys->lastAudioPTS );*/
+        }
+        /* DTiVo Audio with PES Header                      */
+        /* ================================================ */
 
-            /* Check for complete PES */
-            if (check_sync_pes(p_demux, p_block_in, esOffset1,
-                                l_rec_size) == -1)
-            {
-                /* partial PES header found, nothing else.  we're done. */
-                p_sys->i_cur_rec++;
-                return 1;
-            }
-            /* set PCR before we send (if PTS found) */
-            if( p_block_in->i_pts > 0 )
-            {
-                es_out_Control( p_demux->out, ES_OUT_SET_PCR,
-                                p_block_in->i_pts );
-            }
-            /* S2 DTivo has invalid long AC3 packets */
-            if (p_sys->tivo_series == TIVO_SERIES2) {
-                if (p_block_in->i_buffer > AC3_PKT_LENGTH) {
-                    p_block_in->i_buffer -= 2;
-                    p_sys->l_ac3_pkt_size = 0;
-                } else {
-                    p_sys->l_ac3_pkt_size = p_block_in->i_buffer;
-                }
-            }
-            es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
+        /* Check for complete PES */
+        if (check_sync_pes(p_demux, p_block_in, esOffset1,
+                            l_rec_size) == -1)
+        {
+            /* partial PES header found, nothing else. 
+             * we're done. */
+            block_Release(p_block_in);
+            return 0;
         }
-    } /* end "if audio" */
-    /* ================================================================ */
-    /* Closed Caption                                                   */
-    /* ================================================================ */
-    else if ( rec_type == 0x01 )
-    {
-        /*msg_Dbg(p_demux, "CC1 %02x %02x [%c%c]", rec_hdr->ex1,
-                   rec_hdr->ex2, rec_hdr->ex1, rec_hdr->ex2 );*/
-
-        /* construct a 'user-data' MPEG packet */
-        lastCC[ 0x00 ] = 0x00;
-        lastCC[ 0x01 ] = 0x00;
-        lastCC[ 0x02 ] = 0x01;
-        lastCC[ 0x03 ] = 0xb2;
-        lastCC[ 0x04 ] = 'T';    /* vcdimager code says this byte should be 0x11 */
-        lastCC[ 0x05 ] = 'Y';    /* (no other notes there) */
-        lastCC[ 0x06 ] = 0x01;
-        lastCC[ 0x07 ] = rec_hdr->ex1;
-        lastCC[ 0x08 ] = rec_hdr->ex2;
-        /* not sure what to send, because VLC doesn't yet support
-         * teletext type of subtitles (only supports the full-sentence type) */
-        /*p_block_in = block_NewEmpty(); ????
-        es_out_Send( p_demux->out, p_sys->p_subt_es, p_block_in );*/
-    }
-    else if ( rec_type == 0x02 )
-    {
-        /*msg_Dbg(p_demux, "CC2 %02x %02x", rec_hdr->ex1, rec_hdr->ex2 );*/
-
-        /* construct a 'user-data' MPEG packet */
-        lastXDS[ 0x00 ] = 0x00;
-        lastXDS[ 0x01 ] = 0x00;
-        lastXDS[ 0x02 ] = 0x01;
-        lastXDS[ 0x03 ] = 0xb2;
-        lastXDS[ 0x04 ] = 'T';    /* vcdimager code says this byte should be 0x11 */
-        lastXDS[ 0x05 ] = 'Y';    /* (no other notes there) */
-        lastXDS[ 0x06 ] = 0x02;
-        lastXDS[ 0x07 ] = rec_hdr->ex1;
-        lastXDS[ 0x08 ] = rec_hdr->ex2;
-        /* not sure what to send, because VLC doesn't support this?? */
-        /*p_block_in = block_NewEmpty(); ????
-        es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );*/
+#if 0
+        msg_Dbg(p_demux, "packet buffer has "
+                 "%02x %02x %02x %02x %02x %02x %02x %02x "
+                 "%02x %02x %02x %02x %02x %02x %02x %02x",
+                 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
+                 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
+                 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
+                 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
+                 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
+                 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
+                 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
+                 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
+#endif
     }
-    /* ================================================================ */
-    /* Tivo data services (e.g. "thumbs-up to record!")  useless for us */
-    /* ================================================================ */
-    else if ( rec_type == 0x03 )
+    else if( subrec_type == 0x04 )
     {
+        /* SA Audio with no PES Header                      */
+        /* ================================================ */
+        /*msg_Dbg(p_demux,
+                "Adding SA Audio Packet Size %ld", l_rec_size ); */
+
+        if (p_sys->lastAudioPTS > 0)
+            p_block_in->i_pts = p_sys->lastAudioPTS;
     }
-    /* ================================================================ */
-    /* Unknown, but seen regularly */
-    /* ================================================================ */
-    else if ( rec_type == 0x05 )
+    else if( subrec_type == 0x09 )
     {
+        /* DTiVo AC3 Audio Data with PES Header             */
+        /* ================================================ */
+        esOffset1 = find_es_header( ty_AC3AudioPacket,
+                p_block_in->p_buffer, 5 );
+
+#if 0
+        msg_Dbg(p_demux, "buffer has "
+                 "%02x %02x %02x %02x %02x %02x %02x %02x "
+                 "%02x %02x %02x %02x %02x %02x %02x %02x",
+                 p_block_in->p_buffer[0], p_block_in->p_buffer[1],
+                 p_block_in->p_buffer[2], p_block_in->p_buffer[3],
+                 p_block_in->p_buffer[4], p_block_in->p_buffer[5],
+                 p_block_in->p_buffer[6], p_block_in->p_buffer[7],
+                 p_block_in->p_buffer[8], p_block_in->p_buffer[9],
+                 p_block_in->p_buffer[10], p_block_in->p_buffer[11],
+                 p_block_in->p_buffer[12], p_block_in->p_buffer[13],
+                 p_block_in->p_buffer[14], p_block_in->p_buffer[15]);
+        msg_Dbg(p_demux, "audio ES AC3 hdr at offset %d", esOffset1);
+#endif
+
+        /* Check for complete PES */
+        if (check_sync_pes(p_demux, p_block_in, esOffset1,
+                            l_rec_size) == -1)
+        {
+            /* partial PES header found, nothing else.  we're done. */
+            block_Release(p_block_in);
+            return 0;
+        }
+        /* S2 DTivo has invalid long AC3 packets */
+        if (p_sys->tivo_series == TIVO_SERIES2) {
+            if (p_block_in->i_buffer > AC3_PKT_LENGTH) {
+                p_block_in->i_buffer -= 2;
+                p_sys->l_ac3_pkt_size = 0;
+            } else {
+                p_sys->l_ac3_pkt_size = p_block_in->i_buffer;
+            }
+        }
     }
     else
     {
-        msg_Dbg(p_demux, "Invalid record type 0x%02x", rec_type );
-        if (p_block_in) block_Release(p_block_in);
-            invalidType++;
+        /* Unsupported/Unknown */
+        block_Release(p_block_in);
+        return 0;
     }
-    p_sys->i_cur_rec++;
-    return 1;
+
+    /* set PCR before we send (if PTS found) */
+    if( p_block_in->i_pts > 0 )
+        es_out_Control( p_demux->out, ES_OUT_SET_PCR,
+                        p_block_in->i_pts );
+    /* Send data */
+    es_out_Send( p_demux->out, p_sys->p_audio, p_block_in );
+    return 0;
 }
 
+static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    int i_field;
+    int i_channel;
+
+    if( p_block_in )
+        block_Release(p_block_in);
+
+    if( rec_hdr->rec_type == 0x01 )
+        i_field = 0;
+    else if( rec_hdr->rec_type == 0x02 )
+        i_field = 1;
+    else
+        return 0;
+
+    /* XDS data (extract programs infos) transmitted on field 2 only */
+    if( i_field == 1 )
+        DemuxDecodeXds( p_demux, rec_hdr->ex1, rec_hdr->ex2 );
+
+    if( p_sys->cc.i_data + 3 > CC_MAX_DATA_SIZE )
+        return 0;
+
+    p_sys->cc.p_data[p_sys->cc.i_data+0] = i_field;
+    p_sys->cc.p_data[p_sys->cc.i_data+1] = rec_hdr->ex1;
+    p_sys->cc.p_data[p_sys->cc.i_data+2] = rec_hdr->ex2;
+    p_sys->cc.i_data += 3;
+
+    i_channel = cc_Channel( i_field, &p_sys->cc.p_data[p_sys->cc.i_data-3 + 1] );
+    if( i_channel >= 0 && i_channel < 4 )
+        p_sys->cc.pb_present[i_channel] = true;
+    return 0;
+}
 
 /* seek to a position within the stream, if possible */
 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct)
@@ -885,7 +1090,7 @@ static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct)
     /* seek within the chunk to get roughly to where we want */
     p_sys->i_cur_rec = (int)
       ((double) ((seek_pos % CHUNK_SIZE) / (double) (CHUNK_SIZE)) * p_sys->i_num_recs);
-    msg_Dbg(p_demux, "Seeked to file pos " I64Fd, seek_pos);
+    msg_Dbg(p_demux, "Seeked to file pos %"PRId64, seek_pos);
     msg_Dbg(p_demux, " (chunk %d, record %d)",
              p_sys->i_cur_chunk - 1, p_sys->i_cur_rec);
 
@@ -903,6 +1108,371 @@ static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct)
     return VLC_SUCCESS;
 }
 
+/* XDS decoder */
+//#define TY_XDS_DEBUG
+static void XdsInit( xds_t *h )
+{
+    int i, j;
+
+    h->b_xds = false;
+    h->i_class = XDS_MAX_CLASS_COUNT;
+    h->i_type = 0;
+    h->b_future = false;
+    for( i = 0; i < XDS_MAX_CLASS_COUNT; i++ )
+    {
+        for( j = 0; j < 128; j++ )
+            h->pkt[i][j].b_started = false;
+    }
+    h->b_meta_changed = false;
+    memset( &h->meta, 0, sizeof(h->meta) );
+}
+static void XdsExit( xds_t *h )
+{
+    /* */
+    free( h->meta.psz_channel_name );
+    free( h->meta.psz_channel_call_letter );
+    free( h->meta.psz_channel_number );
+
+    /* */
+    free( h->meta.current.psz_name );
+    free( h->meta.current.psz_rating );
+    /* */
+    free( h->meta.future.psz_name );
+    free( h->meta.future.psz_rating );
+}
+static void XdsStringUtf8( char dst[2*32+1], const uint8_t *p_src, int i_src )
+{
+    int i;
+    int i_dst;
+
+    for( i = 0, i_dst = 0; i < i_src; i++ )
+    {
+        switch( p_src[i] )
+        {
+#define E2( c, u1, u2 ) case c: dst[i_dst++] = u1; dst[i_dst++] = u2; break
+        E2( 0x2a, 0xc3,0xa1); // lowercase a, acute accent
+        E2( 0x5c, 0xc3,0xa9); // lowercase e, acute accent
+        E2( 0x5e, 0xc3,0xad); // lowercase i, acute accent
+        E2( 0x5f, 0xc3,0xb3); // lowercase o, acute accent
+        E2( 0x60, 0xc3,0xba); // lowercase u, acute accent
+        E2( 0x7b, 0xc3,0xa7); // lowercase c with cedilla
+        E2( 0x7c, 0xc3,0xb7); // division symbol
+        E2( 0x7d, 0xc3,0x91); // uppercase N tilde
+        E2( 0x7e, 0xc3,0xb1); // lowercase n tilde
+#undef E2
+        default:
+            dst[i_dst++] = p_src[i];
+            break;
+        }
+    }
+    dst[i_dst++] = '\0';
+}
+static bool XdsChangeString( xds_t *h, char **ppsz_dst, const char *psz_new )
+{
+    if( *ppsz_dst && psz_new && !strcmp( *ppsz_dst, psz_new ) )
+        return false;
+    if( *ppsz_dst == NULL && psz_new == NULL )
+        return false;
+
+    free( *ppsz_dst );
+    if( psz_new )
+        *ppsz_dst = strdup( psz_new );
+    else
+        *ppsz_dst = NULL;
+
+    h->b_meta_changed = true;
+    return true;
+}
+
+static void XdsDecodeCurrentFuture( xds_t *h, xds_packet_t *pk )
+{
+    xds_meta_program_t *p_prg = h->b_future ? &h->meta.future : &h->meta.current;
+    char name[2*32+1];
+    int i_rating;
+
+    switch( h->i_type )
+    {
+    case 0x03:
+        XdsStringUtf8( name, pk->p_data, pk->i_data );
+        if( XdsChangeString( h, &p_prg->psz_name, name ) )
+        {
+            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Program Name) %d'\n", pk->i_data );
+            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> program name %s\n", name );
+        }
+        break;
+    case 0x05:
+        i_rating = (pk->p_data[0] & 0x18);
+        if( i_rating == 0x08 )
+        {
+            /* TPG */
+            static const char *pppsz_ratings[8][2] = {
+                { "None",   "No rating (no content advisory)" },
+                { "TV-Y",   "All Children (no content advisory)" },
+                { "TV-Y7",  "Directed to Older Children (V = Fantasy Violence)" },
+                { "TV-G",   "General Audience (no content advisory)" },
+                { "TV-PG",  "Parental Guidance Suggested" },
+                { "TV-14",  "Parents Strongly Cautioned" },
+                { "TV-MA",  "Mature Audience Only" },
+                { "None",   "No rating (no content advisory)" }
+            };
+            p_prg->rating = XDS_META_PROGRAM_RATING_TPG;
+            if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[1]&0x07][0] ) )
+            {
+                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
+                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
+                //         pppsz_ratings[pk->p_data[1]&0x07][0], pppsz_ratings[pk->p_data[1]&0x07][1] );
+            }
+        }
+        else if( i_rating == 0x00 || i_rating == 0x10 )
+        {
+            /* MPAA */
+            static const char *pppsz_ratings[8][2] = {
+                { "N/A",    "N/A" },
+                { "G",      "General Audiences" },
+                { "PG",     "Parental Guidance Suggested" },
+                { "PG-13",  "Parents Strongly Cautioned" },
+                { "R",      "Restricted" },
+                { "NC-17",  "No one 17 and under admitted" },
+                { "X",      "No one under 17 admitted" },
+                { "NR",     "Not Rated" },
+            };
+            p_prg->rating = XDS_META_PROGRAM_RATING_MPAA;
+            if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[0]&0x07][0] ) )
+            {
+                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
+                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n",
+                //         pppsz_ratings[pk->p_data[0]&0x07][0], pppsz_ratings[pk->p_data[0]&0x07][1] );
+            }
+        }
+        else
+        {
+            /* Non US Rating TODO */
+            assert( i_rating == 0x18 ); // only left value possible */
+            p_prg->rating = XDS_META_PROGRAM_RATING_NONE;
+            if( XdsChangeString( h, &p_prg->psz_rating, NULL ) )
+            {
+                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data );
+                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> 0x%2.2x 0x%2.2x\n", pk->p_data[0], pk->p_data[1] );
+            }
+        }
+        break;
+
+    default:
+#ifdef TY_XDS_DEBUG
+        fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Unknown 0x%x)'\n", h->i_type );
+#endif
+        break;
+    }
+}
+
+static void XdsDecodeChannel( xds_t *h, xds_packet_t *pk )
+{
+    char name[2*32+1];
+    char chan[2*32+1];
+
+    switch( h->i_type )
+    {
+    case 0x01:
+        if( pk->i_data < 2 )
+            return;
+        XdsStringUtf8( name, pk->p_data, pk->i_data );
+        if( XdsChangeString( h, &h->meta.psz_channel_name, name ) )
+        {
+            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Name) %d'\n", pk->i_data );
+            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> %s\n", name );
+        }
+        break;
+
+    case 0x02:
+        if( pk->i_data < 4 )
+            return;
+
+        XdsStringUtf8( name, pk->p_data, 4 );
+        if( XdsChangeString( h, &h->meta.psz_channel_call_letter, name ) )
+        {
+            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
+            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> call letter %s\n", name );
+        }
+        if( pk->i_data >= 6 )
+        {
+            XdsStringUtf8( chan, &pk->p_data[4], 2 );
+            if( XdsChangeString( h, &h->meta.psz_channel_number, chan ) )
+            {
+                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
+                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> channel number %s\n", chan );
+            }
+        }
+        else
+        {
+            if( XdsChangeString( h, &h->meta.psz_channel_number, NULL ) )
+            {
+                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data );
+                //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> no channel number letter anymore\n" );
+            }
+        }
+        break;
+    case 0x03:
+        //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Channel Tape Delay)'\n" );
+        break;
+    case 0x04:
+        //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Transmission Signal Identifier)'\n" );
+        break;
+    default:
+#ifdef TY_XDS_DEBUG
+        fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Unknown 0x%x)'\n", h->i_type );
+#endif
+        break;
+    }
+}
+
+static void XdsDecode( xds_t *h, xds_packet_t *pk )
+{
+    switch( h->i_class )
+    {
+    case XDS_CLASS_CURRENT:
+    case XDS_CLASS_FUTURE:
+        XdsDecodeCurrentFuture( h, pk );
+        break;
+    case XDS_CLASS_CHANNEL:
+        XdsDecodeChannel( h, pk );
+        break;
+    case XDS_CLASS_MISCELLANEOUS:
+#ifdef TY_XDS_DEBUG
+        fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Miscellaneous'\n" );
+#endif
+        break;
+    case XDS_CLASS_PUBLIC_SERVICE:
+#ifdef TY_XDS_DEBUG
+        fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Public Service'\n" );
+#endif
+        break;
+    default:
+        //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: unknown class\n" );
+        break;
+    }
+}
+
+static void XdsParse( xds_t *h, uint8_t d1, uint8_t d2 )
+{
+    /* TODO check parity */
+    d1 &= 0x7f;
+    d2 &= 0x7f;
+
+    /* */
+    if( d1 >= 0x01 && d1 <= 0x0e )
+    {
+        const xds_class_t i_class = ( d1 - 1 ) >> 1;
+        const int i_type = d2;
+        const bool b_start = d1 & 0x01;
+        xds_packet_t *pk = &h->pkt[i_class][i_type];
+
+        if( !b_start && !pk->b_started )
+        {
+            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS Continuying a non started packet, ignoring\n" );
+            h->b_xds = false;
+            return;
+        }
+
+        h->b_xds = true;
+        h->i_class = i_class;
+        h->i_type  = i_type;
+        h->b_future = !b_start;
+        pk->b_started = true;
+        if( b_start )
+        {
+            pk->i_data = 0;
+            pk->i_sum = d1 + d2;
+        }
+    }
+    else if( d1 == 0x0f && h->b_xds )
+    {
+        xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
+
+        /* TODO checksum and decode */
+        pk->i_sum += d1 + d2;
+        if( pk->i_sum & 0x7f )
+        {
+            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS invalid checksum, ignoring ---------------------------------\n" );
+            pk->b_started = false;
+            return;
+        }
+        if( pk->i_data <= 0 )
+        {
+            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS empty packet, ignoring ---------------------------------\n" );
+            pk->b_started = false;
+            return;
+        }
+
+        //if( pk->p_data[pk->i_data-1] == 0x40 ) /* Padding byte */
+        //    pk->i_data--;
+        XdsDecode( h, pk );
+
+        /* Reset it */
+        pk->b_started = false;
+    }
+    else if( d1 >= 0x20 && h->b_xds )
+    {
+        xds_packet_t *pk = &h->pkt[h->i_class][h->i_type];
+
+        if( pk->i_data+2 > XDS_MAX_DATA_SIZE )
+        {
+            /* Broken -> reinit */
+            //fprintf( stderr, "xxxxxxxxxxxxxxxXDS broken, reset\n" );
+            h->b_xds = false;
+            pk->b_started = false;
+            return;
+        }
+        /* TODO check parity bit */
+        pk->p_data[pk->i_data++] = d1 & 0x7f;
+        pk->p_data[pk->i_data++] = d2 & 0x7f;
+        pk->i_sum += d1+d2;
+    }
+    else
+    {
+        h->b_xds = false;
+    }
+}
+
+static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    XdsParse( &p_demux->p_sys->xds, d1, d2 );
+    if( p_demux->p_sys->xds.b_meta_changed )
+    {
+        xds_meta_t *m = &p_sys->xds.meta;
+        vlc_meta_t *p_meta;
+        vlc_epg_t *p_epg;
+
+        /* Channel meta data */
+        p_meta = vlc_meta_New();
+        if( m->psz_channel_name )
+            vlc_meta_SetPublisher( p_meta, m->psz_channel_name );
+        if( m->psz_channel_call_letter )
+            vlc_meta_SetTitle( p_meta, m->psz_channel_call_letter );
+        if( m->psz_channel_number )
+            vlc_meta_AddExtra( p_meta, "Channel number", m->psz_channel_number );
+        es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META, TY_ES_GROUP, p_meta );
+        vlc_meta_Delete( p_meta );
+
+        /* Event meta data (current/future) */
+        p_epg = vlc_epg_New( NULL );
+        if( m->current.psz_name )
+        {
+            vlc_epg_AddEvent( p_epg, 0, 0, m->current.psz_name, NULL, NULL );
+            //if( m->current.psz_rating )
+            //  TODO but VLC cannot yet handle rating per epg event
+            vlc_epg_SetCurrent( p_epg, 0 );
+        }
+        if( m->future.psz_name )
+        {
+        }
+        if( p_epg->i_event > 0 )
+            es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG, TY_ES_GROUP, p_epg );
+        vlc_epg_Delete( p_epg );
+    }
+    p_demux->p_sys->xds.b_meta_changed = false;
+}
 
 /* seek to an exact time position within the stream, if possible.
  * l_seek_time is in nanoseconds, the TIVO time standard.
@@ -1065,8 +1635,7 @@ static void parse_master(demux_t *p_demux)
        entire table directly from the stream into memory in place. */
 
     /* clear the SEQ table */
-    if (p_sys->seq_table != NULL)
-        free(p_sys->seq_table);
+    free(p_sys->seq_table);
     
     /* parse header info */
     stream_Read(p_demux->s, mst_buf, 32);
@@ -1092,7 +1661,7 @@ static void parse_master(demux_t *p_demux)
     p_sys->l_first_ty_pts = p_sys->seq_table[0].l_timestamp;
     p_sys->l_final_ty_pts =
         p_sys->seq_table[p_sys->i_seq_table_size - 1].l_timestamp;
-    p_sys->b_have_master = VLC_TRUE;
+    p_sys->b_have_master = true;
 
     i_pts_secs = p_sys->l_first_ty_pts / 1000000000;
     msg_Dbg( p_demux, "first TY pts in master is %02d:%02d:%02d",
@@ -1106,65 +1675,6 @@ static void parse_master(demux_t *p_demux)
 }
 
 
-static int Control(demux_t *p_demux, int i_query, va_list args)
-{
-    demux_sys_t *p_sys = p_demux->p_sys;
-    double f, *pf;
-    int64_t i64, *p_i64;
-
-    /*msg_Info(p_demux, "control cmd %d", i_query);*/
-    switch( i_query )
-    {
-    case DEMUX_GET_POSITION:
-        /* arg is 0.0 - 1.0 percent of overall file position */
-        if( ( i64 = p_sys->i_stream_size ) > 0 )
-        {
-            pf = (double*) va_arg( args, double* );
-            *pf = (double)stream_Tell( p_demux->s ) / (double) i64;
-            return VLC_SUCCESS;
-        }
-        return VLC_EGENERIC;
-
-    case DEMUX_SET_POSITION:
-        /* arg is 0.0 - 1.0 percent of overall file position */
-        f = (double) va_arg( args, double );
-        /* msg_Dbg(p_demux, "Control - set position to %2.3f", f); */
-        if ((i64 = p_sys->i_stream_size) > 0)
-            return ty_stream_seek_pct(p_demux, f);
-        return VLC_EGENERIC;
-    case DEMUX_GET_TIME:
-        /* return TiVo timestamp */
-        p_i64 = (int64_t *) va_arg(args, int64_t *);
-        //*p_i64 = p_sys->lastAudioPTS - p_sys->firstAudioPTS;
-        //*p_i64 = (p_sys->l_last_ty_pts / 1000) + (p_sys->lastAudioPTS -
-        //    p_sys->l_last_ty_pts_sync);
-        *p_i64 = (p_sys->l_last_ty_pts / 1000);
-        return VLC_SUCCESS;
-    case DEMUX_GET_LENGTH:    /* length of program in microseconds, 0 if unk */
-        /* size / bitrate */
-        p_i64 = (int64_t *) va_arg(args, int64_t *);
-        *p_i64 = 0;
-        return VLC_SUCCESS;
-    case DEMUX_SET_TIME:      /* arg is time in microsecs */
-        i64 = (int64_t) va_arg( args, int64_t );
-        return ty_stream_seek_time(p_demux, i64 * 1000);
-    case DEMUX_GET_FPS:
-    default:
-        return VLC_EGENERIC;
-    }
-}
-
-
-/* ======================================================================== */
-static void Close( vlc_object_t *p_this )
-{
-    demux_sys_t *p_sys = ((demux_t *) p_this)->p_sys;
-
-    free(p_sys->rec_hdrs);
-    free(p_sys);
-}
-
-
 /* ======================================================================== */
 /* "Peek" at some chunks.  Skip over the Part header if we find it.
  * We parse the peeked data and determine audio type,
@@ -1176,7 +1686,7 @@ static int probe_stream(demux_t *p_demux)
     demux_sys_t *p_sys = p_demux->p_sys;
     const uint8_t *p_buf;
     int i;
-    vlc_bool_t b_probe_error = VLC_FALSE;
+    bool b_probe_error = false;
 
     /* we need CHUNK_PEEK_COUNT chunks of data, first one might be a Part header, so ... */
     if (stream_Peek( p_demux->s, &p_buf, CHUNK_PEEK_COUNT * CHUNK_SIZE ) <
@@ -1199,15 +1709,15 @@ static int probe_stream(demux_t *p_demux)
     /* the final tally */
     if (p_sys->tivo_series == TIVO_SERIES_UNKNOWN) {
         msg_Err(p_demux, "Can't determine Tivo Series.");
-        b_probe_error = VLC_TRUE;
+        b_probe_error = true;
     }
     if (p_sys->audio_type == TIVO_AUDIO_UNKNOWN) {
         msg_Err(p_demux, "Can't determine Tivo Audio Type.");
-        b_probe_error = VLC_TRUE;
+        b_probe_error = true;
     }
     if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) {
         msg_Err(p_demux, "Can't determine Tivo Type (SA/DTivo).");
-        b_probe_error = VLC_TRUE;
+        b_probe_error = true;
     }
     return b_probe_error?VLC_EGENERIC:VLC_SUCCESS;
 }
@@ -1379,12 +1889,11 @@ static int get_chunk_header(demux_t *p_demux)
         p_sys->i_seq_rec = p_peek[1];
     }
     p_sys->i_cur_rec = 0;
-    p_sys->b_first_chunk = VLC_FALSE;
+    p_sys->b_first_chunk = false;
   
     /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/
 
-    if (p_sys->rec_hdrs)
-        free(p_sys->rec_hdrs);
+    free(p_sys->rec_hdrs);
 
     /* skip past the 4 bytes we "peeked" earlier */
     stream_Read( p_demux->s, NULL, 4 );
@@ -1392,7 +1901,8 @@ static int get_chunk_header(demux_t *p_demux)
     /* read the record headers into a temp buffer */
     p_hdr_buf = malloc(i_num_recs * 16);
     if (stream_Read(p_demux->s, p_hdr_buf, i_num_recs * 16) < i_num_recs * 16) {
-        p_sys->eof = VLC_TRUE;
+        free( p_hdr_buf );
+        p_sys->eof = true;
         return 0;
     }
     /* parse them */
@@ -1439,16 +1949,17 @@ static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf
             p_rec_hdr->ex2 = b2;
             p_rec_hdr->l_rec_size = 0;
             p_rec_hdr->l_ty_pts = 0;
-            p_rec_hdr->b_ext = VLC_TRUE;
+            p_rec_hdr->b_ext = true;
         }
         else
         {
             p_rec_hdr->l_rec_size = ( record_header[ 0 ] << 8 |
                 record_header[ 1 ] ) << 4 | ( record_header[ 2 ] >> 4 );
             *pi_payload_size += p_rec_hdr->l_rec_size;
-            p_rec_hdr->b_ext = VLC_FALSE;
+            p_rec_hdr->b_ext = false;
             p_rec_hdr->l_ty_pts = U64_AT( &record_header[ 8 ] );
         }
+        //fprintf( stderr, "parse_chunk_headers[%d] t=0x%x s=%d\n", i, p_rec_hdr->rec_type, p_rec_hdr->subrec_type );
     } /* end of record-header loop */
     return p_hdrs;
 }