]> git.sesse.net Git - vlc/blobdiff - modules/demux/ts.c
Fixed AVI palette support.
[vlc] / modules / demux / ts.c
index 68a61c5925add1d497d47ce6c1ba17c4403da161..9d2ef54ce44e3e634fe51a838f5fff297c54aade 100644 (file)
@@ -30,7 +30,7 @@
 # include "config.h"
 #endif
 
-#include <vlc/vlc.h>
+#include <vlc_common.h>
 #include <vlc_plugin.h>
 
 #include <ctype.h>
  *  - ...
  */
 
+/*****************************************************************************
+ * Callback prototypes
+ *****************************************************************************/
+static int ChangeKeyCallback    ( vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void * );
+
 /*****************************************************************************
  * Module descriptor
  *****************************************************************************/
 static int  Open  ( vlc_object_t * );
 static void Close ( vlc_object_t * );
 
+/* TODO
+ * - Rename "extra pmt" to "user pmt"
+ * - Update extra pmt description
+ *      pmt_pid[:pmt_number][=pid_description[,pid_description]]
+ *      where pid_description could take 3 forms:
+ *          1. pid:pcr (to force the pcr pid)
+ *          2. pid:stream_type
+ *          3. pid:type=fourcc where type=(video|audio|spu)
+ */
 #define PMT_TEXT N_("Extra PMT")
 #define PMT_LONGTEXT N_( \
   "Allows a user to specify an extra pmt (pmt_pid=pid:stream_type[,...])." )
@@ -113,6 +127,10 @@ static void Close ( vlc_object_t * );
 #define CSA_TEXT N_("CSA ck")
 #define CSA_LONGTEXT N_("Control word for the CSA encryption algorithm")
 
+#define CSA2_TEXT N_("Second CSA Key")
+#define CSA2_LONGTEXT N_("The even CSA encryption key. This must be a " \
+  "16 char string (8 hexadecimal bytes).")
+
 #define SILENT_TEXT N_("Silent mode")
 #define SILENT_LONGTEXT N_("Do not complain on encrypted PES.")
 
@@ -149,6 +167,7 @@ vlc_module_begin();
     add_integer( "ts-out-mtu", 1400, NULL, MTUOUT_TEXT,
                  MTUOUT_LONGTEXT, true );
     add_string( "ts-csa-ck", NULL, NULL, CSA_TEXT, CSA_LONGTEXT, true );
+    add_string( "ts-csa2-ck", NULL, NULL, CSA_TEXT, CSA_LONGTEXT, true );
     add_integer( "ts-csa-pkt", 188, NULL, CPKT_TEXT, CPKT_LONGTEXT, true );
     add_bool( "ts-silent", 0, NULL, SILENT_TEXT, SILENT_LONGTEXT, true );
 
@@ -166,6 +185,14 @@ vlc_module_end();
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
+static const char *const ppsz_teletext_type[] = {
+ "",
+ N_("Teletext"),
+ N_("Teletext subtitles"),
+ N_("Teletext additional information"),
+ N_("Teletext programme schedule"),
+ N_("Teletext hearing impaired subtitles")
+};
 
 typedef struct
 {
@@ -311,6 +338,8 @@ typedef struct
 
 struct demux_sys_t
 {
+    vlc_mutex_t     csa_lock;
+
     /* TS packet size (188, 192, 204) */
     int         i_packet_size;
 
@@ -321,6 +350,7 @@ struct demux_sys_t
     ts_pid_t    pid[8192];
 
     /* All PMT */
+    bool        b_user_pmt;
     int         i_pmt;
     ts_pid_t    **pmt;
 
@@ -348,6 +378,9 @@ struct demux_sys_t
 
     /* */
     bool        b_meta;
+
+    /* */
+    bool        b_start_record;
 };
 
 static int Demux    ( demux_t *p_demux );
@@ -377,6 +410,9 @@ static void PCRHandle( demux_t *p_demux, ts_pid_t *, block_t * );
 static iod_descriptor_t *IODNew( int , uint8_t * );
 static void              IODFree( iod_descriptor_t * );
 
+#define TS_USER_PMT_NUMBER (0)
+static int UserPmt( demux_t *p_demux, const char * );
+
 #define TS_PACKET_SIZE_188 188
 #define TS_PACKET_SIZE_192 192
 #define TS_PACKET_SIZE_204 204
@@ -539,6 +575,7 @@ static int Open( vlc_object_t *p_this )
         return VLC_ENOMEM;
     memset( p_sys, 0, sizeof( demux_sys_t ) );
     p_sys->i_packet_size = i_packet_size;
+    vlc_mutex_init( &p_sys->csa_lock );
 
     /* Fill dump mode fields */
     p_sys->i_write = 0;
@@ -616,6 +653,7 @@ static int Open( vlc_object_t *p_this )
     p_sys->b_udp_out = false;
     p_sys->i_ts_read = 50;
     p_sys->csa = NULL;
+    p_sys->b_start_record = false;
 
     /* Init PAT handler */
     pat = &p_sys->pid[0];
@@ -698,132 +736,59 @@ static int Open( vlc_object_t *p_this )
     /* We handle description of an extra PMT */
     var_Create( p_demux, "ts-extra-pmt", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
     var_Get( p_demux, "ts-extra-pmt", &val );
-    if( val.psz_string && strchr( val.psz_string, '=' ) != NULL )
-    {
-        char *psz = val.psz_string;
-        int  i_pid = strtol( psz, &psz, 0 );
-
-        if( i_pid >= 2 && i_pid < 8192 )
-        {
-            ts_pid_t *pmt = &p_sys->pid[i_pid];
-            ts_prg_psi_t *prg;
-
-            msg_Dbg( p_demux, "extra pmt specified (pid=%d)", i_pid );
-            PIDInit( pmt, true, NULL );
-
-            /* Dummy PMT */
-            prg = malloc( sizeof( ts_prg_psi_t ) );
-            if( !prg )
-            {
-                msg_Err( p_demux, "out of memory" );
-                Close( VLC_OBJECT(p_demux) );
-                return VLC_ENOMEM;
-            }
-
-            memset( prg, 0, sizeof( ts_prg_psi_t ) );
-            prg->i_pid_pcr  = -1;
-            prg->i_pid_pmt  = -1;
-            prg->i_number   = 0;    /* special */
-            prg->handle     = dvbpsi_AttachPMT( 1, (dvbpsi_pmt_callback)PMTCallBack, p_demux );
-            TAB_APPEND( pmt->psi->i_prg, pmt->psi->prg, prg );
-
-            psz = strchr( psz, '=' ) + 1;   /* can't failed */
-            while( psz && *psz )
-            {
-                char *psz_next = strchr( psz, ',' );
-                int i_pid, i_stream_type;
-
-                if( psz_next )
-                {
-                    *psz_next++ = '\0';
-                }
-
-                i_pid = strtol( psz, &psz, 0 );
-                if( *psz == ':' )
-                {
-                    i_stream_type = strtol( psz + 1, &psz, 0 );
-                    if( i_pid >= 2 && i_pid < 8192 &&
-                        !p_sys->pid[i_pid].b_valid )
-                    {
-                        ts_pid_t *pid = &p_sys->pid[i_pid];
-
-                        PIDInit( pid, false, pmt->psi);
-                        if( pmt->psi->prg[0]->i_pid_pcr <= 0 )
-                        {
-                            pmt->psi->prg[0]->i_pid_pcr = i_pid;
-                        }
-                        PIDFillFormat( pid, i_stream_type);
-                        if( pid->es->fmt.i_cat != UNKNOWN_ES )
-                        {
-                            if( p_sys->b_es_id_pid )
-                            {
-                                pid->es->fmt.i_id = i_pid;
-                            }
-                            msg_Dbg( p_demux, "  * es pid=%d type=%d "
-                                     "fcc=%4.4s", i_pid, i_stream_type,
-                                     (char*)&pid->es->fmt.i_codec );
-                            pid->es->id = es_out_Add( p_demux->out,
-                                                      &pid->es->fmt );
-                        }
-                    }
-                }
-                psz = psz_next;
-            }
-        }
-    }
+    p_sys->b_user_pmt = false;
+    if( val.psz_string && *val.psz_string )
+        UserPmt( p_demux, val.psz_string );
     free( val.psz_string );
 
-    var_Create( p_demux, "ts-csa-ck", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
+    var_Create( p_demux, "ts-csa-ck", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND );
     var_Get( p_demux, "ts-csa-ck", &val );
     if( val.psz_string && *val.psz_string )
     {
-        char *psz = val.psz_string;
-        if( psz[0] == '0' && ( psz[1] == 'x' || psz[1] == 'X' ) )
+        int i_res;
+        vlc_value_t csa2;
+
+        p_sys->csa = csa_New();
+
+        var_Create( p_demux, "ts-csa2-ck", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND);
+        var_Get( p_demux, "ts-csa2-ck", &csa2 );
+        i_res = csa_SetCW( (vlc_object_t*)p_demux, p_sys->csa, val.psz_string, true );
+        if( i_res == VLC_SUCCESS && csa2.psz_string && *csa2.psz_string )
         {
-            psz += 2;
+            if( csa_SetCW( (vlc_object_t*)p_demux, p_sys->csa, csa2.psz_string, false ) != VLC_SUCCESS )
+            {
+                csa_SetCW( (vlc_object_t*)p_demux, p_sys->csa, val.psz_string, false );
+            }
         }
-        if( strlen( psz ) != 16 )
+        else if ( i_res == VLC_SUCCESS )
         {
-            msg_Warn( p_demux, "invalid csa ck (it must be 16 chars long)" );
+            csa_SetCW( (vlc_object_t*)p_demux, p_sys->csa, val.psz_string, false );
         }
         else
         {
-#ifndef UNDER_CE
-            uint64_t i_ck = strtoull( psz, NULL, 16 );
-#else
-            uint64_t i_ck = strtoll( psz, NULL, 16 );
-#endif
-            uint8_t ck[8];
-            int     i;
-            for( i = 0; i < 8; i++ )
-            {
-                ck[i] = ( i_ck >> ( 56 - 8*i) )&0xff;
-            }
-#ifndef TS_NO_CSA_CK_MSG
-            msg_Dbg( p_demux, "using CSA scrambling with "
-                     "ck=%x:%x:%x:%x:%x:%x:%x:%x",
-                     ck[0], ck[1], ck[2], ck[3], ck[4], ck[5], ck[6], ck[7] );
-#endif
-            p_sys->csa = csa_New();
+            csa_Delete( p_sys->csa );
+            p_sys->csa = NULL;
+        }
 
-            if( p_sys->csa )
-            {
-                vlc_value_t pkt_val;
+        if( p_sys->csa )
+        {
+            vlc_value_t pkt_val;
 
-                csa_SetCW( p_sys->csa, ck, ck );
+            var_AddCallback( p_demux, "ts-csa-ck", ChangeKeyCallback, (void *)1 );
+            var_AddCallback( p_demux, "ts-csa2-ck", ChangeKeyCallback, NULL );
 
-                var_Create( p_demux, "ts-csa-pkt", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
-                var_Get( p_demux, "ts-csa-pkt", &pkt_val );
-                if( pkt_val.i_int < 4 || pkt_val.i_int > 188 )
-                {
-                    msg_Err( p_demux, "wrong packet size %d specified.", pkt_val.i_int );
-                    msg_Warn( p_demux, "using default packet size of 188 bytes" );
-                    p_sys->i_csa_pkt_size = 188;
-                }
-                else p_sys->i_csa_pkt_size = pkt_val.i_int;
-                msg_Dbg( p_demux, "decrypting %d bytes of packet", p_sys->i_csa_pkt_size );
+            var_Create( p_demux, "ts-csa-pkt", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
+            var_Get( p_demux, "ts-csa-pkt", &pkt_val );
+            if( pkt_val.i_int < 4 || pkt_val.i_int > 188 )
+            {
+                msg_Err( p_demux, "wrong packet size %d specified.", pkt_val.i_int );
+                msg_Warn( p_demux, "using default packet size of 188 bytes" );
+                p_sys->i_csa_pkt_size = 188;
             }
+            else p_sys->i_csa_pkt_size = pkt_val.i_int;
+            msg_Dbg( p_demux, "decrypting %d bytes of packet", p_sys->i_csa_pkt_size );
         }
+        free( csa2.psz_string );
     }
     free( val.psz_string );
 
@@ -895,10 +860,15 @@ static void Close( vlc_object_t *p_this )
         net_Close( p_sys->fd );
         free( p_sys->buffer );
     }
+    vlc_mutex_lock( &p_sys->csa_lock );
     if( p_sys->csa )
     {
+        var_DelCallback( p_demux, "ts-csa-ck", ChangeKeyCallback, NULL );
+        var_DelCallback( p_demux, "ts-csa2-ck", ChangeKeyCallback, NULL );
         csa_Delete( p_sys->csa );
+        p_sys->csa = NULL;
     }
+    vlc_mutex_unlock( &p_sys->csa_lock );
 
     if( p_sys->i_pmt ) free( p_sys->pmt );
 
@@ -927,9 +897,32 @@ static void Close( vlc_object_t *p_this )
     free( p_sys->psz_file );
     p_sys->psz_file = NULL;
 
+    vlc_mutex_destroy( &p_sys->csa_lock );
     free( p_sys );
 }
 
+/*****************************************************************************
+ * ChangeKeyCallback: called when changing the odd encryption key on the fly.
+ *****************************************************************************/
+static int ChangeKeyCallback( vlc_object_t *p_this, char const *psz_cmd,
+                           vlc_value_t oldval, vlc_value_t newval,
+                           void *p_data )
+{
+    VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
+    demux_t     *p_demux = (demux_t*)p_this;
+    demux_sys_t *p_sys = p_demux->p_sys;
+    int         i_tmp = (intptr_t)p_data;
+
+    vlc_mutex_lock( &p_sys->csa_lock );
+    if ( i_tmp )
+        i_tmp = csa_SetCW( p_this, p_sys->csa, newval.psz_string, true );
+    else
+        i_tmp = csa_SetCW( p_this, p_sys->csa, newval.psz_string, false );
+
+    vlc_mutex_unlock( &p_sys->csa_lock );
+    return i_tmp;
+}
+
 /*****************************************************************************
  * DemuxFile:
  *****************************************************************************/
@@ -960,13 +953,13 @@ static int DemuxFile( demux_t *p_demux )
         if( p_sys->buffer[i_pos] != 0x47 )
         {
             msg_Warn( p_demux, "lost sync" );
-            while( !p_demux->b_die && (i_pos < i_data) )
+            while( vlc_object_alive (p_demux) && (i_pos < i_data) )
             {
                 i_pos++;
                 if( p_sys->buffer[i_pos] == 0x47 )
                     break;
             }
-            if( !p_demux->b_die )
+            if( vlc_object_alive (p_demux) )
                 msg_Warn( p_demux, "sync found" );
         }
 
@@ -1019,7 +1012,11 @@ static int DemuxFile( demux_t *p_demux )
 
         /* Test if user wants to decrypt it first */
         if( p_sys->csa )
+        {
+            vlc_mutex_lock( &p_sys->csa_lock );
             csa_Decrypt( p_demux->p_sys->csa, &p_buffer[i_pos], p_demux->p_sys->i_csa_pkt_size );
+            vlc_mutex_unlock( &p_sys->csa_lock );
+        }
 
         i_pos += p_sys->i_packet_size;
     }
@@ -1067,7 +1064,7 @@ static int Demux( demux_t *p_demux )
             msg_Warn( p_demux, "lost synchro" );
             block_Release( p_pkt );
 
-            while( !p_demux->b_die )
+            while( vlc_object_alive (p_demux) )
             {
                 const uint8_t *p_peek;
                 int i_peek, i_skip = 0;
@@ -1106,6 +1103,13 @@ static int Demux( demux_t *p_demux )
             }
         }
 
+        if( p_sys->b_start_record )
+        {
+            /* Enable recording once synchronized */
+            stream_Control( p_demux->s, STREAM_SET_RECORD_STATE, true, "ts" );
+            p_sys->b_start_record = false;
+        }
+
         if( p_sys->b_udp_out )
         {
             memcpy( &p_sys->buffer[i_pkt * p_sys->i_packet_size],
@@ -1205,6 +1209,7 @@ 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;
+    bool b_bool, *pb_bool;
     int64_t i64;
     int64_t *pi64;
     int i_int;
@@ -1401,6 +1406,19 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
             return VLC_SUCCESS;
         }
 
+        case DEMUX_CAN_RECORD:
+            pb_bool = (bool*)va_arg( args, bool * );
+            *pb_bool = true;
+            return VLC_SUCCESS;
+
+        case DEMUX_SET_RECORD_STATE:
+            b_bool = (bool)va_arg( args, int );
+
+            if( !b_bool )
+                stream_Control( p_demux->s, STREAM_SET_RECORD_STATE, false );
+            p_sys->b_start_record = b_bool;
+            return VLC_SUCCESS;
+
         case DEMUX_GET_FPS:
         case DEMUX_SET_TIME:
         default:
@@ -1408,6 +1426,131 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
     }
 }
 
+/*****************************************************************************
+ *
+ *****************************************************************************/
+static int UserPmt( demux_t *p_demux, const char *psz_fmt )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    char *psz_dup = strdup( psz_fmt );
+    char *psz = psz_dup;
+    int  i_pid;
+    int  i_number;
+
+    if( !psz_dup )
+        return VLC_ENOMEM;
+
+    /* Parse PID */
+    i_pid = strtol( psz, &psz, 0 );
+    if( i_pid < 2 || i_pid >= 8192 )
+        goto error;
+
+    /* Parse optional program number */
+    i_number = 0;
+    if( *psz == ':' )
+        i_number = strtol( &psz[1], &psz, 0 );
+
+    /* */
+    ts_pid_t *pmt = &p_sys->pid[i_pid];
+    ts_prg_psi_t *prg;
+
+    msg_Dbg( p_demux, "user pmt specified (pid=%d,number=%d)", i_pid, i_number );
+    PIDInit( pmt, true, NULL );
+
+    /* Dummy PMT */
+    prg = malloc( sizeof( ts_prg_psi_t ) );
+    if( !prg )
+        goto error;
+
+    memset( prg, 0, sizeof( ts_prg_psi_t ) );
+    prg->i_pid_pcr  = -1;
+    prg->i_pid_pmt  = -1;
+    prg->i_version  = -1;
+    prg->i_number   = i_number != 0 ? i_number : TS_USER_PMT_NUMBER;
+    prg->handle     = dvbpsi_AttachPMT( i_number != TS_USER_PMT_NUMBER ? i_number : 1, (dvbpsi_pmt_callback)PMTCallBack, p_demux );
+    TAB_APPEND( pmt->psi->i_prg, pmt->psi->prg, prg );
+
+    psz = strchr( psz, '=' );
+    if( psz )
+        psz++;
+    while( psz && *psz )
+    {
+        char *psz_next = strchr( psz, ',' );
+        int i_pid;
+
+        if( psz_next )
+            *psz_next++ = '\0';
+
+        i_pid = strtol( psz, &psz, 0 );
+        if( *psz != ':' || i_pid < 2 || i_pid >= 8192 )
+            goto next;
+
+        char *psz_opt = &psz[1];
+        if( !strcmp( psz_opt, "pcr" ) )
+        {
+            prg->i_pid_pcr = i_pid;
+        }
+        else if( !p_sys->pid[i_pid].b_valid )
+        {
+            ts_pid_t *pid = &p_sys->pid[i_pid];
+
+            char *psz_arg = strchr( psz_opt, '=' );
+            if( psz_arg )
+                *psz_arg++ = '\0';
+
+            PIDInit( pid, false, pmt->psi);
+            if( prg->i_pid_pcr <= 0 )
+                prg->i_pid_pcr = i_pid;
+
+            if( psz_arg && strlen( psz_arg ) == 4 )
+            {
+                const vlc_fourcc_t i_codec = VLC_FOURCC( psz_arg[0], psz_arg[1],
+                                                         psz_arg[2], psz_arg[3] );
+                int i_cat = UNKNOWN_ES;
+                es_format_t *fmt = &pid->es->fmt;
+
+                if( !strcmp( psz_opt, "video" ) )
+                    i_cat = VIDEO_ES;
+                else if( !strcmp( psz_opt, "audio" ) )
+                    i_cat = AUDIO_ES;
+                else if( !strcmp( psz_opt, "spu" ) )
+                    i_cat = SPU_ES;
+
+                es_format_Init( fmt, i_cat, i_codec );
+                fmt->b_packetized = false;
+            }
+            else
+            {
+                const int i_stream_type = strtol( psz_opt, NULL, 0 );
+                PIDFillFormat( pid, i_stream_type );
+            }
+            pid->es->fmt.i_group = i_number;
+            if( p_sys->b_es_id_pid )
+                pid->es->fmt.i_id = i_pid;
+
+            if( pid->es->fmt.i_cat != UNKNOWN_ES )
+            {
+                msg_Dbg( p_demux, "  * es pid=%d fcc=%4.4s", i_pid,
+                         (char*)&pid->es->fmt.i_codec );
+                pid->es->id = es_out_Add( p_demux->out,
+                                          &pid->es->fmt );
+            }
+        }
+
+    next:
+        psz = psz_next;
+    }
+
+    p_sys->b_user_pmt = true;
+    TAB_APPEND( p_sys->i_pmt, p_sys->pmt, pmt );
+    free( psz_dup );
+    return VLC_SUCCESS;
+
+error:
+    free( psz_dup );
+    return VLC_EGENERIC;
+}
+
 static void PIDInit( ts_pid_t *pid, bool b_psi, ts_psi_t *p_owner )
 {
     bool b_old_valid = pid->b_valid;
@@ -1560,8 +1703,8 @@ static void ParsePES( demux_t *p_demux, ts_pid_t *pid )
         case 0xBC:  /* Program stream map */
         case 0xBE:  /* Padding */
         case 0xBF:  /* Private stream 2 */
-        case 0xB0:  /* ECM */
-        case 0xB1:  /* EMM */
+        case 0xF0:  /* ECM */
+        case 0xF1:  /* EMM */
         case 0xFF:  /* Program stream directory */
         case 0xF2:  /* DSMCC stream */
         case 0xF8:  /* ITU-T H.222.1 type E stream */
@@ -1811,7 +1954,9 @@ static bool GatherPES( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk )
 
     if( p_demux->p_sys->csa )
     {
+        vlc_mutex_lock( &p_demux->p_sys->csa_lock );
         csa_Decrypt( p_demux->p_sys->csa, p_bk->p_buffer, p_demux->p_sys->i_csa_pkt_size );
+        vlc_mutex_unlock( &p_demux->p_sys->csa_lock );
     }
 
     if( !b_adaptation )
@@ -2800,11 +2945,14 @@ static void EITCallBack( demux_t *p_demux, dvbpsi_eit_t *p_eit )
                     if( pE->i_text_length > 0 )
                     {
                         char *psz_text = EITConvertToUTF8( pE->i_text, pE->i_text_length );
-                        msg_Dbg( p_demux, "       - text='%s'", psz_text );
+                        if( psz_text )
+                        {
+                            msg_Dbg( p_demux, "       - text='%s'", psz_text );
 
-                        psz_extra = realloc( psz_extra, strlen(psz_extra) + strlen(psz_text) + 1 );
-                        strcat( psz_extra, psz_text );
-                        free( psz_text );
+                            psz_extra = realloc( psz_extra, strlen(psz_extra) + strlen(psz_text) + 1 );
+                            strcat( psz_extra, psz_text );
+                            free( psz_text );
+                        }
                     }
 
                     for( i = 0; i < pE->i_entry_count; i++ )
@@ -2916,8 +3064,8 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
         int i_prg;
         for( i_prg = 0; i_prg < p_sys->pmt[i]->psi->i_prg; i_prg++ )
         {
-            if( p_sys->pmt[i]->psi->prg[i_prg]->i_number ==
-                p_pmt->i_program_number )
+            const int i_pmt_number = p_sys->pmt[i]->psi->prg[i_prg]->i_number;
+            if( i_pmt_number != TS_USER_PMT_NUMBER && i_pmt_number == p_pmt->i_program_number )
             {
                 pmt = p_sys->pmt[i];
                 prg = p_sys->pmt[i]->psi->prg[i_prg];
@@ -3188,7 +3336,8 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
                         else if( !memcmp( p_dr->p_data, "BSSD", 4 ) )
                         {
                             pid->es->fmt.i_cat = AUDIO_ES;
-                            pid->es->fmt.i_codec = VLC_FOURCC('l','p','c','m');
+                            pid->es->fmt.b_packetized = true;
+                            pid->es->fmt.i_codec = VLC_FOURCC('a','e','s','3');
                         }
                         else
                         {
@@ -3262,8 +3411,8 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
                         for( n = 0; n < sub->i_pages_number; n++ )
                         {
                             dvbpsi_teletextpage_t *p_page = &sub->p_pages[n];
-                            if( (p_page->i_teletext_type == 0x2)
-                                 || (p_page->i_teletext_type == 0x5) )
+                            if( p_page->i_teletext_type > 0x0 &&
+                                p_page->i_teletext_type < 0x6 )
                             {
                                 ts_es_t *p_es;
 
@@ -3299,36 +3448,21 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
                                             p_page->i_iso6392_language_code, 3 );
                                     p_es->fmt.psz_language[3] = 0;
                                 }
-                                switch( p_page->i_teletext_type )
-                                {
-                                case 0x2:
-                                    p_es->fmt.psz_description =
-                                        strdup(_("Teletext subtitles"));
-                                    msg_Dbg( p_demux,
-                                             "    * sub lan=%s page=%d%x",
-                                             p_es->fmt.psz_language,
-                                             p_page->i_teletext_magazine_number,
-                                             p_page->i_teletext_page_number );
-                                    break;
+                                p_es->fmt.psz_description = strdup(_(ppsz_teletext_type[p_page->i_teletext_type]));
 
-                                case 0x5:
-                                    p_es->fmt.psz_description =
-                                        strdup(_("Teletext hearing impaired subtitles"));
-                                    msg_Dbg( p_demux,
-                                             "    * hearing impaired lan=%s page=%d%x",
+                                msg_Dbg( p_demux,
+                                             "    * ttxt type=%s lan=%s page=%d%02x",
+                                             p_es->fmt.psz_description,
                                              p_es->fmt.psz_language,
                                              p_page->i_teletext_magazine_number,
                                              p_page->i_teletext_page_number );
-                                    break;
-                                default:
-                                    break;
-                                }
 
+                                /* Hack, FIXME This stores the initial page for this track,
+                                   so that it can be used by the telx and zvbi decoders. */
                                 p_es->fmt.subs.dvb.i_id =
                                     p_page->i_teletext_page_number;
-                                /* Hack, FIXME */
                                 p_es->fmt.subs.dvb.i_id |=
-                                ((int)p_page->i_teletext_magazine_number << 16);
+                                    ((int)p_page->i_teletext_magazine_number << 16);
 
                                 i++;
                             }
@@ -3503,6 +3637,35 @@ static void PMTCallBack( demux_t *p_demux, dvbpsi_pmt_t *p_pmt )
                 }
             }
         }
+        else if( p_es->i_type == 0xd1 )
+        {
+            dvbpsi_descriptor_t *p_dr;
+
+            for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
+                 p_dr = p_dr->p_next )
+            {
+                msg_Dbg( p_demux, "  * es pid=%d type=%d dr->i_tag=0x%x",
+                         p_es->i_pid, p_es->i_type, p_dr->i_tag );
+
+                if( p_dr->i_tag == 0x05 )
+                {
+                    /* Registration Descriptor */
+                    if( !memcmp( p_dr->p_data, "drac", 4 ) )
+                    {
+                        /* registration descriptor for Dirac
+                         * (backwards compatable with VC-2 (SMPTE Sxxxx:2008)) */
+                        pid->es->fmt.i_cat = VIDEO_ES;
+                        pid->es->fmt.i_codec = VLC_FOURCC('d','r','a','c');
+                    }
+                    else
+                    {
+                        msg_Warn( p_demux,
+                                  "unknown Registration Descriptor (%4.4s)",
+                                  p_dr->p_data );
+                    }
+                }
+            }
+        }
         else if( p_es->i_type == 0xa0 )
         {
             /* MSCODEC sent by vlc */
@@ -3761,9 +3924,10 @@ static void PATCallBack( demux_t *p_demux, dvbpsi_pat_t *p_pat )
 
     msg_Dbg( p_demux, "PATCallBack called" );
 
-    if( pat->psi->i_pat_version != -1 &&
-        ( !p_pat->b_current_next ||
-          p_pat->i_version == pat->psi->i_pat_version ) )
+    if( ( pat->psi->i_pat_version != -1 &&
+            ( !p_pat->b_current_next ||
+              p_pat->i_version == pat->psi->i_pat_version ) ) ||
+        p_sys->b_user_pmt )
     {
         dvbpsi_DeletePAT( p_pat );
         return;
@@ -3856,8 +4020,7 @@ static void PATCallBack( demux_t *p_demux, dvbpsi_pat_t *p_pat )
             for( i_prg = 0; i_prg < pmt_rm[i]->psi->i_prg; i_prg++ )
             {
                 const int i_number = pmt_rm[i]->psi->prg[i_prg]->i_number;
-                if( i_number != 0 )
-                    es_out_Control( p_demux->out, ES_OUT_DEL_GROUP, i_number );
+                es_out_Control( p_demux->out, ES_OUT_DEL_GROUP, i_number );
             }
 
             PIDClean( p_demux->out, &p_sys->pid[pmt_rm[i]->i_pid] );