]> git.sesse.net Git - vlc/blobdiff - modules/demux/ty.c
Vobsub: language is not necessarily on 2 characters
[vlc] / modules / demux / ty.c
index f9e0514b6de647c78e3fcfd8ef868dea670a0171..75e2f3cb3a185b8c054210c1166bb59e595f01de 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_codec.h>
+#include <vlc_meta.h>
+#include <vlc_input.h>
 #include "../codec/cc.h"
 
+#include <assert.h>
+
 /*****************************************************************************
  * Module descriptor
  *****************************************************************************/
 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_category( CAT_INPUT );
-    set_subcategory( SUBCAT_INPUT_DEMUX );
-    set_capability("demux2", 6);
+vlc_module_begin ()
+    set_shortname( N_("TY") )
+    set_description(N_("TY Stream audio/video demux"))
+    set_category( CAT_INPUT )
+    set_subcategory( SUBCAT_INPUT_DEMUX )
+    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.
      */
-    set_callbacks( Open, Close );
-    add_shortcut("ty");
-    add_shortcut("tivo");
-vlc_module_end();
+    set_callbacks( Open, Close )
+    add_shortcut("ty", "tivo")
+vlc_module_end ()
 
 /*****************************************************************************
  * Local prototypes
@@ -95,7 +103,7 @@ static const uint8_t ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };
  b/e0: video B-frame header start
  c/e0: video GOP header start
  e/01: closed-caption data
- e/02: Extended data services data 
+ e/02: Extended data services data
  e/03: ipreview data ("thumbs up to record" signal)
  e/05: UK Teletext
 */
@@ -107,10 +115,10 @@ static const uint8_t ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };
 typedef struct
 {
   long l_rec_size;
-  uint8_t ex1, ex2;
+  uint8_t ex[2];
   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;
 
@@ -141,6 +149,69 @@ 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 */
@@ -149,12 +220,14 @@ struct demux_sys_t
   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 */
@@ -167,8 +240,8 @@ struct demux_sys_t
   //mtime_t         l_last_ty_pts_sync; /* audio PTS at time of last TY PTS */
   uint64_t        l_first_ty_pts;     /* first TY PTS in this master chunk */
   uint64_t        l_final_ty_pts;     /* final TY PTS in this master chunk */
-  int             i_seq_table_size;   /* number of entries in SEQ table */
-  int             i_bits_per_seq_entry; /* # of bits in SEQ table bitmask */
+  unsigned        i_seq_table_size;   /* number of entries in SEQ table */
+  unsigned        i_bits_per_seq_entry; /* # of bits in SEQ table bitmask */
 
   mtime_t         firstAudioPTS;
   mtime_t         lastAudioPTS;
@@ -179,8 +252,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 *);
@@ -190,7 +263,7 @@ static int find_es_header( const uint8_t *header,
 static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct);
 static int ty_stream_seek_time(demux_t *, uint64_t);
 
-static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf,
+static ty_rec_hdr_t *parse_chunk_headers( const uint8_t *p_buf,
                                           int i_num_recs, int *pi_payload_size);
 static int probe_stream(demux_t *p_demux);
 static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk);
@@ -202,6 +275,11 @@ static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block
 
 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
  *
@@ -230,14 +308,14 @@ static int Open(vlc_object_t *p_this)
          U32_AT(&p_peek[8]) != CHUNK_SIZE )
     {
         if( !p_demux->b_force &&
-            !demux2_IsPathExtension( p_demux, ".ty" ) &&
-            !demux2_IsPathExtension( p_demux, ".ty+" ) )
+            !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..." );
     }
 
-       /* at this point, we assume we have a valid TY stream */  
+    /* at this point, we assume we have a valid TY stream */
     msg_Dbg( p_demux, "valid TY stream detected" );
 
     /* Set exported functions */
@@ -249,9 +327,11 @@ 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->lastAudioPTS  = VLC_TS_INVALID;
+    p_sys->lastVideoPTS  = VLC_TS_INVALID;
     p_sys->i_stream_size = stream_Size(p_demux->s);
     p_sys->tivo_type = TIVO_TYPE_UNKNOWN;
     p_sys->audio_type = TIVO_AUDIO_UNKNOWN;
@@ -259,7 +339,7 @@ static int Open(vlc_object_t *p_this)
     p_sys->i_Pes_Length = 0;
     p_sys->i_Pts_Offset = 0;
     p_sys->l_ac3_pkt_size = 0;
-  
+
     /* see if this stream is seekable */
     stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
 
@@ -273,14 +353,16 @@ static int Open(vlc_object_t *p_this)
 
     /* register the proper audio codec */
     if (p_sys->audio_type == TIVO_AUDIO_MPEG) {
-        es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'm', 'p', 'g', 'a' ) );
+        es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_MPGA );
     } else {
-        es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', ' ' ) );
+        es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_A52 );
     }
+    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' ) );
+    es_format_Init( &fmt, VIDEO_ES, VLC_CODEC_MPGV );
+    fmt.i_group = TY_ES_GROUP;
     p_sys->p_video = es_out_Add( p_demux->out, &fmt );
 
     /* */
@@ -288,6 +370,8 @@ static int Open(vlc_object_t *p_this)
         p_sys->p_cc[i] = NULL;
     cc_Init( &p_sys->cc );
 
+    XdsInit( &p_sys->xds );
+
     return VLC_SUCCESS;
 }
 
@@ -307,7 +391,7 @@ static int Demux( demux_t *p_demux )
     block_t      *p_block_in = NULL;
 
     /*msg_Dbg(p_demux, "ty demux processing" );*/
-   
+
     /* did we hit EOF earlier? */
     if( p_sys->eof )
         return 0;
@@ -323,7 +407,7 @@ static int Demux( demux_t *p_demux )
     * - 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 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 */
@@ -343,7 +427,7 @@ static int Demux( demux_t *p_demux )
         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 )
         {
@@ -358,7 +442,7 @@ static int Demux( demux_t *p_demux )
 
         /* set these as 'unknown' for now */
         p_block_in->i_pts =
-        p_block_in->i_dts = 0;
+        p_block_in->i_dts = VLC_TS_INVALID;
     }
     /*else
     {
@@ -422,7 +506,7 @@ static int Control(demux_t *p_demux, int i_query, va_list args)
         if( ( i64 = p_sys->i_stream_size ) > 0 )
         {
             pf = (double*) va_arg( args, double* );
-            *pf = (double)stream_Tell( p_demux->s ) / (double) i64;
+            *pf = ((double)1.0) * stream_Tell( p_demux->s ) / (double) i64;
             return VLC_SUCCESS;
         }
         return VLC_EGENERIC;
@@ -462,10 +546,10 @@ 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 );
-    if( p_sys->seq_table )
-        free( p_sys->seq_table );
+    free( p_sys->seq_table );
     free(p_sys);
 }
 
@@ -524,7 +608,7 @@ static int check_sync_pes( demux_t *p_demux, block_t *p_block,
         if( offset < 0 )
         {
             /* no header found, fake some 00's (this works, believe me) */
-            memset( p_sys->pes_buffer, 4, 0 );
+            memset( p_sys->pes_buffer, 0, 4 );
             p_sys->i_pes_buf_cnt = 4;
             if( rec_len > 4 )
                 msg_Err( p_demux, "PES header not found in record of %d bytes!",
@@ -545,12 +629,12 @@ static int check_sync_pes( demux_t *p_demux, block_t *p_block,
         return -1;    /* partial PES, no audio data */
     }
     /* full PES header present, extract PTS */
-    p_sys->lastAudioPTS = get_pts( &p_block->p_buffer[ offset +
-            p_sys->i_Pts_Offset ] );
+    p_sys->lastAudioPTS = VLC_TS_0 + get_pts( &p_block->p_buffer[ offset +
+                                   p_sys->i_Pts_Offset ] );
     if (p_sys->firstAudioPTS < 0)
         p_sys->firstAudioPTS = p_sys->lastAudioPTS;
     p_block->i_pts = p_sys->lastAudioPTS;
-    /*msg_Dbg(p_demux, "Audio PTS %lld", p_sys->lastAudioPTS );*/
+    /*msg_Dbg(p_demux, "Audio PTS %"PRId64, p_sys->lastAudioPTS );*/
     /* adjust audio record to remove PES header */
     memmove(p_block->p_buffer + offset, p_block->p_buffer + offset +
             p_sys->i_Pes_Length, rec_len - p_sys->i_Pes_Length);
@@ -608,9 +692,9 @@ static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl
         {
             //msg_Dbg(p_demux, "Video PES hdr in pkt type 0x%02x at offset %d",
                 //subrec_type, esOffset1);
-            p_sys->lastVideoPTS = get_pts(
+            p_sys->lastVideoPTS = VLC_TS_0 + 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,
+            /*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 */
@@ -665,13 +749,13 @@ static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl
             //p_sys->l_last_ty_pts += 33366667;
         }
         /* set PTS for this block before we send */
-        if (p_sys->lastVideoPTS > 0)
+        if (p_sys->lastVideoPTS > VLC_TS_INVALID)
         {
             p_block_in->i_pts = p_sys->lastVideoPTS;
-            /* PTS gets used ONCE. 
+            /* 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;
+            p_sys->lastVideoPTS = VLC_TS_INVALID;
         }
     }
 
@@ -697,32 +781,26 @@ static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl
             continue;
 
         es_format_Init( &fmt, SPU_ES, fcc[i] );
-        fmt.psz_description = strdup( _(ppsz_description[i]) );
+        fmt.psz_description = strdup( vlc_gettext(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 );
 
     }
     /* Send the CC data */
-    if( p_block_in->i_pts > 0 && p_sys->cc.i_data > 0 )
+    if( p_block_in->i_pts > VLC_TS_INVALID && p_sys->cc.i_data > 0 )
     {
-        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++ )
         {
-            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
+            if( p_sys->p_cc[i] )
+            {
+                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 );
+
                 es_out_Send( p_demux->out, p_sys->p_cc[i], p_cc );
+            }
         }
         cc_Flush( &p_sys->cc );
     }
@@ -744,10 +822,10 @@ static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl
         return -1;
 #if 0
         int i;
-        printf( "Audio Packet Header " );
+        fprintf( stderr, "Audio Packet Header " );
         for( i = 0 ; i < 24 ; i++ )
-            printf( "%2.2x ", p_block_in->p_buffer[i] );
-        printf( "\n" );
+            fprintf( stderr, "%2.2x ", p_block_in->p_buffer[i] );
+        fprintf( stderr, "\n" );
 #endif
 
     if( subrec_type == 2 )
@@ -794,7 +872,7 @@ static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl
             }
             else
             {
-                p_sys->lastAudioPTS = get_pts( 
+                p_sys->lastAudioPTS = VLC_TS_0 + get_pts(
                     &p_sys->pes_buffer[ esOffset1 + p_sys->i_Pts_Offset ] );
                 p_block_in->i_pts = p_sys->lastAudioPTS;
             }
@@ -833,15 +911,14 @@ static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl
         /* ================================================ */
         if ( ( esOffset1 == 0 ) && ( l_rec_size == 16 ) )
         {
-            p_sys->lastAudioPTS = get_pts( &p_block_in->p_buffer[
+            p_sys->lastAudioPTS = VLC_TS_0 + get_pts( &p_block_in->p_buffer[
                         SA_PTS_OFFSET ] );
             if (p_sys->firstAudioPTS < 0)
                 p_sys->firstAudioPTS = p_sys->lastAudioPTS;
 
             block_Release(p_block_in);
             return 0;
-            /*msg_Dbg(p_demux, "SA Audio PTS %lld",
-                       p_sys->lastAudioPTS );*/
+            /*msg_Dbg(p_demux, "SA Audio PTS %"PRId64, p_sys->lastAudioPTS );*/
         }
         /* DTiVo Audio with PES Header                      */
         /* ================================================ */
@@ -850,7 +927,7 @@ static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl
         if (check_sync_pes(p_demux, p_block_in, esOffset1,
                             l_rec_size) == -1)
         {
-            /* partial PES header found, nothing else. 
+            /* partial PES header found, nothing else.
              * we're done. */
             block_Release(p_block_in);
             return 0;
@@ -876,7 +953,7 @@ static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl
         /*msg_Dbg(p_demux,
                 "Adding SA Audio Packet Size %ld", l_rec_size ); */
 
-        if (p_sys->lastAudioPTS > 0)
+        if (p_sys->lastAudioPTS > VLC_TS_INVALID )
             p_block_in->i_pts = p_sys->lastAudioPTS;
     }
     else if( subrec_type == 0x09 )
@@ -927,7 +1004,7 @@ static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl
     }
 
     /* set PCR before we send (if PTS found) */
-    if( p_block_in->i_pts > 0 )
+    if( p_block_in->i_pts > VLC_TS_INVALID )
         es_out_Control( p_demux->out, ES_OUT_SET_PCR,
                         p_block_in->i_pts );
     /* Send data */
@@ -939,7 +1016,6 @@ static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block
 {
     demux_sys_t *p_sys = p_demux->p_sys;
     int i_field;
-    int i_channel;
 
     if( p_block_in )
         block_Release(p_block_in);
@@ -953,19 +1029,12 @@ static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block
 
     /* XDS data (extract programs infos) transmitted on field 2 only */
     if( i_field == 1 )
-        DemuxDecodeXds( p_demux, rec_hdr->ex1, rec_hdr->ex2 );
+        DemuxDecodeXds( p_demux, rec_hdr->ex[0], rec_hdr->ex[1] );
 
     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+1] );
-    if( i_channel >= 0 && i_channel < 4 )
-        p_sys->cc.pb_present[i_channel] = VLC_TRUE;
+    cc_AppendData( &p_sys->cc, i_field, rec_hdr->ex );
     return 0;
 }
 
@@ -974,8 +1043,8 @@ static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct)
 {
     demux_sys_t *p_sys = p_demux->p_sys;
     int64_t seek_pos = p_sys->i_stream_size * seek_pct;
-    int i, i_cur_part;
-    long l_skip_amt;
+    uint64_t l_skip_amt;
+    unsigned i_cur_part;
 
     /* if we're not seekable, there's nothing to do */
     if (!p_sys->b_seekable)
@@ -984,7 +1053,7 @@ static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct)
     /* figure out which part & chunk we want & go there */
     i_cur_part = seek_pos / TIVO_PART_LENGTH;
     p_sys->i_cur_chunk = seek_pos / CHUNK_SIZE;
-    
+
     /* try to read the part header (master chunk) if it's there */
     if ( stream_Seek( p_demux->s, i_cur_part * TIVO_PART_LENGTH ))
     {
@@ -1002,31 +1071,387 @@ static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct)
     /* load the chunk */
     p_sys->i_stuff_cnt = 0;
     get_chunk_header(p_demux);
-  
+
     /* 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);
 
     /* seek to the start of this record's data.
      * to do that, we have to skip past all prior records */
     l_skip_amt = 0;
-    for (i=0; i<p_sys->i_cur_rec; i++)
+    for ( int i=0; i<p_sys->i_cur_rec; i++)
         l_skip_amt += p_sys->rec_hdrs[i].l_rec_size;
     stream_Seek(p_demux->s, ((p_sys->i_cur_chunk-1) * CHUNK_SIZE) +
                  (p_sys->i_num_recs * 16) + l_skip_amt + 4);
 
     /* to hell with syncing any audio or video, just start reading records... :) */
-    /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = 0;*/
-    es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
+    /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = VLC_TS_INVALID;*/
     return VLC_SUCCESS;
 }
 
+/* XDS decoder */
+//#define TY_XDS_DEBUG
+static void XdsInit( xds_t *h )
+{
+    h->b_xds = false;
+    h->i_class = XDS_MAX_CLASS_COUNT;
+    h->i_type = 0;
+    h->b_future = false;
+    for( int i = 0; i < XDS_MAX_CLASS_COUNT; i++ )
+    {
+        for( int 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_dst = 0;
+    for( int i = 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 )
 {
-    /* TODO */
+    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.
@@ -1035,18 +1460,19 @@ static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 )
 static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time)
 {
     demux_sys_t *p_sys = p_demux->p_sys;
-    int i, i_seq_entry = 0;
+    unsigned i_seq_entry = 0;
+    unsigned i;
     int i_skip_cnt;
-    long l_cur_pos = stream_Tell(p_demux->s);
-    int i_cur_part = l_cur_pos / TIVO_PART_LENGTH;
-    long l_seek_secs = l_seek_time / 1000000000;
+    int64_t l_cur_pos = stream_Tell(p_demux->s);
+    unsigned i_cur_part = l_cur_pos / TIVO_PART_LENGTH;
+    uint64_t l_seek_secs = l_seek_time / 1000000000;
     uint64_t l_fwd_stamp = 1;
 
     /* if we're not seekable, there's nothing to do */
     if (!p_sys->b_seekable || !p_sys->b_have_master)
         return VLC_EGENERIC;
 
-    msg_Dbg(p_demux, "Skipping to time %02ld:%02ld:%02ld",
+    msg_Dbg(p_demux, "Skipping to time %02"PRIu64":%02"PRIu64":%02"PRIu64,
             l_seek_secs / 3600, (l_seek_secs / 60) % 60, l_seek_secs % 60);
 
     /* seek to the proper segment if necessary */
@@ -1091,7 +1517,7 @@ static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time)
             break;
         }
     }
-    
+
     /* if we went through the entire last loop and didn't find our target,
        then we skip to the next part.  What has happened is that the actual
        time we're seeking is within this part, but there isn't a SEQ hdr
@@ -1107,18 +1533,18 @@ static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time)
         i_cur_part++;
         parse_master(p_demux);
         i_seq_entry = 0;
-    }     
-     
+    }
+
     /* determine which chunk has our seek_time */
-    for (i=0; i<p_sys->i_bits_per_seq_entry; i++) {
-        long l_chunk_nr = i_seq_entry * p_sys->i_bits_per_seq_entry + i;
-        long l_chunk_offset = (l_chunk_nr + 1) * CHUNK_SIZE;
-        msg_Dbg(p_demux, "testing part %d chunk %ld mask 0x%02X bit %d",
+    for (unsigned i=0; i<p_sys->i_bits_per_seq_entry; i++) {
+        uint64_t l_chunk_nr = i_seq_entry * p_sys->i_bits_per_seq_entry + i;
+        uint64_t l_chunk_offset = (l_chunk_nr + 1) * CHUNK_SIZE;
+        msg_Dbg(p_demux, "testing part %d chunk %"PRIu64" mask 0x%02X bit %d",
             i_cur_part, l_chunk_nr,
             p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8], i%8);
         if (p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8] & (1 << (i%8))) {
             /* check this chunk's SEQ header timestamp */
-            msg_Dbg(p_demux, "has SEQ. seeking to chunk at 0x%lX",
+            msg_Dbg(p_demux, "has SEQ. seeking to chunk at 0x%"PRIu64,
                 (i_cur_part * TIVO_PART_LENGTH) + l_chunk_offset);
             stream_Seek(p_demux->s, (i_cur_part * TIVO_PART_LENGTH) +
                 l_chunk_offset);
@@ -1137,7 +1563,7 @@ static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time)
             }
             l_seek_secs = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts /
                 1000000000;
-            msg_Dbg(p_demux, "found SEQ hdr for timestamp %02ld:%02ld:%02ld",
+            msg_Dbg(p_demux, "found SEQ hdr for timestamp %02"PRIu64":%02"PRIu64":%02"PRIu64,
                 l_seek_secs / 3600,
                 (l_seek_secs / 60) % 60, l_seek_secs % 60);
             if (p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts >= l_seek_time) {
@@ -1161,8 +1587,8 @@ static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time)
        so we need to skip past any stream data prior to the seq_rec
        in this chunk */
     i_skip_cnt = 0;
-    for (i=0; i<p_sys->i_seq_rec; i++)
-        i_skip_cnt += p_sys->rec_hdrs[i].l_rec_size;
+    for (int j=0; j<p_sys->i_seq_rec; j++)
+        i_skip_cnt += p_sys->rec_hdrs[j].l_rec_size;
     stream_Read(p_demux->s, NULL, i_skip_cnt);
     p_sys->i_cur_rec = p_sys->i_seq_rec;
     //p_sys->l_last_ty_pts = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts;
@@ -1179,7 +1605,7 @@ static void parse_master(demux_t *p_demux)
 {
     demux_sys_t *p_sys = p_demux->p_sys;
     uint8_t mst_buf[32];
-    int i, i_map_size;
+    uint32_t i, i_map_size;
     int64_t i_save_pos = stream_Tell(p_demux->s);
     int64_t i_pts_secs;
 
@@ -1190,9 +1616,8 @@ 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);
     i_map_size = U32_AT(&mst_buf[20]);  /* size of bitmask, in bytes */
@@ -1201,14 +1626,20 @@ static void parse_master(demux_t *p_demux)
     p_sys->i_seq_table_size = i / (8 + i_map_size);
 
     /* parse all the entries */
-    p_sys->seq_table = malloc(p_sys->i_seq_table_size * sizeof(ty_seq_table_t));
-    for (i=0; i<p_sys->i_seq_table_size; i++) {
-        stream_Read(p_demux->s, mst_buf, 8 + i_map_size);
+    p_sys->seq_table = calloc(p_sys->i_seq_table_size, sizeof(ty_seq_table_t));
+    if (p_sys->seq_table == NULL)
+    {
+        p_sys->i_seq_table_size = 0;
+        return;
+    }
+    for (unsigned i=0; i<p_sys->i_seq_table_size; i++) {
+        stream_Read(p_demux->s, mst_buf, 8);
         p_sys->seq_table[i].l_timestamp = U64_AT(&mst_buf[0]);
         if (i_map_size > 8) {
             msg_Err(p_demux, "Unsupported SEQ bitmap size in master chunk");
-            memset(p_sys->seq_table[i].chunk_bitmask, i_map_size, 0);
+            stream_Read(p_demux->s, NULL, i_map_size);
         } else {
+            stream_Read(p_demux->s, mst_buf + 8, i_map_size);
             memcpy(p_sys->seq_table[i].chunk_bitmask, &mst_buf[8], i_map_size);
         }
     }
@@ -1217,14 +1648,16 @@ 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",
-             (int)(i_pts_secs / 3600), (int)((i_pts_secs / 60) % 60), (int)(i_pts_secs % 60) );
+    msg_Dbg( p_demux,
+             "first TY pts in master is %02"PRId64":%02"PRId64":%02"PRId64,
+             i_pts_secs / 3600, (i_pts_secs / 60) % 60, i_pts_secs % 60 );
     i_pts_secs = p_sys->l_final_ty_pts / 1000000000;
-    msg_Dbg( p_demux, "final TY pts in master is %02d:%02d:%02d",
-             (int)(i_pts_secs / 3600), (int)((i_pts_secs / 60) % 60), (int)(i_pts_secs % 60) );
+    msg_Dbg( p_demux,
+             "final TY pts in master is %02"PRId64":%02"PRId64":%02"PRId64,
+             i_pts_secs / 3600, (i_pts_secs / 60) % 60, i_pts_secs % 60 );
 
     /* seek past this chunk */
     stream_Seek(p_demux->s, i_save_pos + CHUNK_SIZE);
@@ -1242,7 +1675,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 ) <
@@ -1251,7 +1684,7 @@ static int probe_stream(demux_t *p_demux)
         /* TODO: if seekable, then loop reading chunks into a temp buffer */
         return VLC_EGENERIC;
     }
-    
+
     /* the real work: analyze this chunk */
     for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
         analyze_chunk(p_demux, p_buf);
@@ -1261,19 +1694,19 @@ static int probe_stream(demux_t *p_demux)
             break;
         p_buf += CHUNK_SIZE;
     }
-    
+
     /* 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;
 }
@@ -1287,7 +1720,7 @@ static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk)
     int i_num_recs, i;
     ty_rec_hdr_t *p_hdrs;
     int i_num_6e0, i_num_be0, i_num_9c0, i_num_3c0;
-    uint32_t i_payload_size;
+    int i_payload_size;
 
     /* skip if it's a Part header */
     if( U32_AT( &p_chunk[ 0 ] ) == TIVO_PES_FILEID )
@@ -1300,10 +1733,10 @@ static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk)
         /* try again with the next chunk.  Sometimes there are dead ones */
         return;
     }
-    
+
     p_chunk += 4;       /* skip past rec count & SEQ bytes */
     //msg_Dbg(p_demux, "probe: chunk has %d recs", i_num_recs);
-    p_hdrs = parse_chunk_headers(p_demux, p_chunk, i_num_recs, &i_payload_size);
+    p_hdrs = parse_chunk_headers(p_chunk, i_num_recs, &i_payload_size);
     /* scan headers.
      * 1. check video packets.  Presence of 0x6e0 means S1.
      *    No 6e0 but have be0 means S2.
@@ -1411,14 +1844,14 @@ static int get_chunk_header(demux_t *p_demux)
     /* read the TY packet header */
     i_readSize = stream_Peek( p_demux->s, &p_peek, 4 );
     p_sys->i_cur_chunk++;
-  
+
     if ( (i_readSize < 4) || ( U32_AT(&p_peek[ 0 ] ) == 0 ))
     {
         /* EOF */
         p_sys->eof = 1;
         return 0;
     }
-  
+
     /* check if it's a PART Header */
     if( U32_AT( &p_peek[ 0 ] ) == TIVO_PES_FILEID )
     {
@@ -1426,7 +1859,7 @@ static int get_chunk_header(demux_t *p_demux)
         parse_master(p_demux);
         return get_chunk_header(p_demux);
     }
-    
+
     /* number of records in chunk (8- or 16-bit number) */
     if (p_peek[3] & 0x80)
     {
@@ -1445,12 +1878,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 );
@@ -1459,11 +1891,11 @@ static int get_chunk_header(demux_t *p_demux)
     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) {
         free( p_hdr_buf );
-        p_sys->eof = VLC_TRUE;
+        p_sys->eof = true;
         return 0;
     }
     /* parse them */
-    p_sys->rec_hdrs = parse_chunk_headers(p_demux, p_hdr_buf, i_num_recs,
+    p_sys->rec_hdrs = parse_chunk_headers(p_hdr_buf, i_num_recs,
             &i_payload_size);
     free(p_hdr_buf);
 
@@ -1476,7 +1908,7 @@ static int get_chunk_header(demux_t *p_demux)
 }
 
 
-static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf,
+static ty_rec_hdr_t *parse_chunk_headers( const uint8_t *p_buf,
                                           int i_num_recs, int *pi_payload_size)
 {
     int i;
@@ -1495,25 +1927,23 @@ static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf
         {
             uint8_t b1, b2;
             /* marker bit 2 set, so read extended data */
-            b1 = ( ( ( record_header[ 0 ] & 0x0f ) << 4 ) | 
+            b1 = ( ( ( record_header[ 0 ] & 0x0f ) << 4 ) |
                    ( ( record_header[ 1 ] & 0xf0 ) >> 4 ) );
-            b1 &= 0x7f;
-            b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) | 
+            b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) |
                    ( ( record_header[ 2 ] & 0xf0 ) >> 4 ) );
-            b2 &= 0x7f;
 
-            p_rec_hdr->ex1 = b1;
-            p_rec_hdr->ex2 = b2;
+            p_rec_hdr->ex[0] = b1;
+            p_rec_hdr->ex[1] = 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 );