From efc1cee95dc48555898191fb07e0194098402f93 Mon Sep 17 00:00:00 2001
From: Francois Cartegnie <fcvlcdev@free.fr>
Date: Wed, 3 Feb 2016 13:49:58 +0100
Subject: [PATCH] demux: ts: split the beast

---
 modules/demux/Makefile.am               |   21 +-
 modules/demux/mpeg/mpeg4_iod.h          |   14 +-
 modules/demux/mpeg/pes.h                |   11 +-
 modules/demux/mpeg/ps.h                 |    1 +
 modules/demux/mpeg/sections.c           |    8 +-
 modules/demux/mpeg/sections.h           |    7 -
 modules/demux/mpeg/timestamps.h         |   37 +
 modules/demux/mpeg/ts.c                 | 3777 +----------------------
 modules/demux/mpeg/ts.h                 |  110 +-
 modules/demux/mpeg/ts_hotfixes.c        |  354 +++
 modules/demux/mpeg/ts_hotfixes.h        |   27 +
 modules/demux/mpeg/ts_pid.c             |  268 ++
 modules/demux/mpeg/ts_pid.h             |  118 +
 modules/demux/mpeg/ts_psi.c             | 1784 +++++++++++
 modules/demux/mpeg/ts_psi.h             |   35 +
 modules/demux/mpeg/ts_psi_eit.c         |  554 ++++
 modules/demux/mpeg/ts_psi_eit.h         |   24 +
 modules/demux/mpeg/ts_scte.c            |   91 +
 modules/demux/mpeg/ts_scte.h            |   27 +
 modules/demux/mpeg/ts_sl.c              |  204 ++
 modules/demux/mpeg/ts_sl.h              |   29 +
 modules/demux/mpeg/ts_streams.c         |  305 ++
 modules/demux/mpeg/ts_streams.h         |   47 +
 modules/demux/mpeg/ts_streams_private.h |  128 +
 po/POTFILES.in                          |    1 +
 25 files changed, 4292 insertions(+), 3690 deletions(-)
 create mode 100644 modules/demux/mpeg/timestamps.h
 create mode 100644 modules/demux/mpeg/ts_hotfixes.c
 create mode 100644 modules/demux/mpeg/ts_hotfixes.h
 create mode 100644 modules/demux/mpeg/ts_pid.c
 create mode 100644 modules/demux/mpeg/ts_pid.h
 create mode 100644 modules/demux/mpeg/ts_psi.c
 create mode 100644 modules/demux/mpeg/ts_psi.h
 create mode 100644 modules/demux/mpeg/ts_psi_eit.c
 create mode 100644 modules/demux/mpeg/ts_psi_eit.h
 create mode 100644 modules/demux/mpeg/ts_scte.c
 create mode 100644 modules/demux/mpeg/ts_scte.h
 create mode 100644 modules/demux/mpeg/ts_sl.c
 create mode 100644 modules/demux/mpeg/ts_sl.h
 create mode 100644 modules/demux/mpeg/ts_streams.c
 create mode 100644 modules/demux/mpeg/ts_streams.h
 create mode 100644 modules/demux/mpeg/ts_streams_private.h

diff --git a/modules/demux/Makefile.am b/modules/demux/Makefile.am
index cf0fd8c8bcbb..8518ef774868 100644
--- a/modules/demux/Makefile.am
+++ b/modules/demux/Makefile.am
@@ -229,14 +229,27 @@ libplaylist_plugin_la_SOURCES = \
 demux_LTLIBRARIES += libplaylist_plugin.la
 
 libts_plugin_la_SOURCES = demux/mpeg/ts.c demux/mpeg/ts.h \
+        demux/mpeg/ts_pid.h demux/mpeg/ts_pid.c \
+        demux/mpeg/ts_psi.h demux/mpeg/ts_psi.c \
+        demux/mpeg/ts_psi_eit.h demux/mpeg/ts_psi_eit.c \
+        demux/mpeg/ts_streams.h demux/mpeg/ts_streams.c \
+        demux/mpeg/ts_scte.h demux/mpeg/ts_scte.c \
         demux/mpeg/sections.c demux/mpeg/sections.h \
         demux/mpeg/mpeg4_iod.c demux/mpeg/mpeg4_iod.h \
-        demux/mpeg/ts_strings.h demux/mpeg/pes.h \
-	mux/mpeg/csa.c mux/mpeg/dvbpsi_compat.h \
-	mux/mpeg/streams.h mux/mpeg/tables.c mux/mpeg/tables.h \
+        demux/mpeg/ts_sl.c demux/mpeg/ts_sl.h \
+        demux/mpeg/ts_hotfixes.c demux/mpeg/ts_hotfixes.h \
+        demux/mpeg/ts_strings.h \
+        demux/mpeg/pes.h \
+        demux/mpeg/timestamps.h \
+        demux/dvb-text.h \
+        demux/opus.h \
+	mux/mpeg/csa.c \
+        mux/mpeg/dvbpsi_compat.h \
+	mux/mpeg/streams.h \
+        mux/mpeg/tables.c mux/mpeg/tables.h \
 	mux/mpeg/tsutil.c mux/mpeg/tsutil.h \
         codec/scte18.h \
-	demux/dvb-text.h codec/opus_header.c demux/opus.h
+	codec/opus_header.c
 libts_plugin_la_CFLAGS = $(AM_CFLAGS) $(DVBPSI_CFLAGS)
 libts_plugin_la_LIBADD = $(DVBPSI_LIBS) $(SOCKET_LIBS)
 if HAVE_ARIBB24
diff --git a/modules/demux/mpeg/mpeg4_iod.h b/modules/demux/mpeg/mpeg4_iod.h
index 4fca1306fb07..2ae91fd14ed6 100644
--- a/modules/demux/mpeg/mpeg4_iod.h
+++ b/modules/demux/mpeg/mpeg4_iod.h
@@ -17,6 +17,8 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
+#ifndef VLC_MPEG4_IOD_H
+#define VLC_MPEG4_IOD_H
 
 #define ES_DESCRIPTOR_COUNT 255
 typedef enum
@@ -61,7 +63,10 @@ typedef struct
     mtime_t i_pts;
 } sl_header_data;
 
-typedef struct
+typedef struct es_mpeg4_descriptor_t es_mpeg4_descriptor_t;
+typedef struct decoder_config_descriptor_t decoder_config_descriptor_t;
+
+struct decoder_config_descriptor_t
 {
     uint8_t                 i_objectTypeIndication;
     uint8_t                 i_streamType;
@@ -69,9 +74,9 @@ typedef struct
     unsigned                i_extra;
     uint8_t                 *p_extra;
 
-} decoder_config_descriptor_t;
+};
 
-typedef struct
+struct es_mpeg4_descriptor_t
 {
     bool                    b_ok;
     uint16_t                i_es_id;
@@ -81,7 +86,7 @@ typedef struct
     decoder_config_descriptor_t    dec_descr;
     sl_config_descriptor_t         sl_descr;
 
-} es_mpeg4_descriptor_t;
+};
 
 typedef struct
 {
@@ -105,3 +110,4 @@ sl_header_data DecodeSLHeader( unsigned i_data, const uint8_t *p_data,
                                const sl_config_descriptor_t *sl );
 void DecodeODCommand( vlc_object_t *p_object, od_descriptors_t *,
                       unsigned i_data, const uint8_t *p_data );
+#endif
diff --git a/modules/demux/mpeg/pes.h b/modules/demux/mpeg/pes.h
index d76ba93f97ec..1decfc05a827 100644
--- a/modules/demux/mpeg/pes.h
+++ b/modules/demux/mpeg/pes.h
@@ -17,12 +17,8 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
-
-#define FROM_SCALE_NZ(x) ((x) * 100 / 9)
-#define TO_SCALE_NZ(x)   ((x) * 9 / 100)
-
-#define FROM_SCALE(x) (VLC_TS_0 + FROM_SCALE_NZ(x))
-#define TO_SCALE(x)   TO_SCALE_NZ((x) - VLC_TS_0)
+#ifndef VLC_MPEG_PES_H
+#define VLC_MPEG_PES_H
 
 static inline mtime_t ExtractPESTimestamp( const uint8_t *p_data )
 {
@@ -44,6 +40,7 @@ static inline mtime_t ExtractMPEG1PESTimestamp( const uint8_t *p_data )
              (mtime_t)(p_data[4] >> 3);
 }
 
+inline
 static int ParsePESHeader( vlc_object_t *p_object, const uint8_t *p_header, size_t i_header,
                            unsigned *pi_skip, mtime_t *pi_dts, mtime_t *pi_pts,
                            uint8_t *pi_stream_id )
@@ -140,3 +137,5 @@ static int ParsePESHeader( vlc_object_t *p_object, const uint8_t *p_header, size
     *pi_skip = i_skip;
     return VLC_SUCCESS;
 }
+
+#endif
diff --git a/modules/demux/mpeg/ps.h b/modules/demux/mpeg/ps.h
index 07e58dce654e..ceb1244ecb68 100644
--- a/modules/demux/mpeg/ps.h
+++ b/modules/demux/mpeg/ps.h
@@ -24,6 +24,7 @@
 #include <assert.h>
 #include <vlc_demux.h>
 #include <vlc_memory.h>
+#include "timestamps.h"
 
 /* 256-0xC0 for normal stream, 256 for 0xbd stream, 256 for 0xfd stream, 8 for 0xa0 AOB stream */
 #define PS_TK_COUNT (256+256+256+8 - 0xc0)
diff --git a/modules/demux/mpeg/sections.c b/modules/demux/mpeg/sections.c
index 3a5573c9648b..45aad3cb491c 100644
--- a/modules/demux/mpeg/sections.c
+++ b/modules/demux/mpeg/sections.c
@@ -17,10 +17,16 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
-#include "sections.h"
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
 
+#include <vlc_common.h>
 #include <vlc_block.h>
 
+#include "ts_pid.h"
+#include "sections.h"
+
 typedef struct ts_sections_assembler_t
 {
     int8_t i_version;
diff --git a/modules/demux/mpeg/sections.h b/modules/demux/mpeg/sections.h
index caa0f1faae5c..eb6d90a0c9dc 100644
--- a/modules/demux/mpeg/sections.h
+++ b/modules/demux/mpeg/sections.h
@@ -20,13 +20,6 @@
 #ifndef TS_SECTIONS_H
 #define TS_SECTIONS_H
 
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "ts.h"
-#include <vlc_common.h>
-
 typedef void(* ts_section_callback_t)( demux_t *, ts_pid_t *, block_t * );
 typedef struct ts_sections_processor_t ts_sections_processor_t;
 
diff --git a/modules/demux/mpeg/timestamps.h b/modules/demux/mpeg/timestamps.h
new file mode 100644
index 000000000000..57f4fb08025a
--- /dev/null
+++ b/modules/demux/mpeg/timestamps.h
@@ -0,0 +1,37 @@
+/*****************************************************************************
+ * timestamps.h: MPEG TS/PS Timestamps helpers
+ *****************************************************************************
+ * Copyright (C) 2004-2016 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifndef VLC_MPEG_TIMESTAMPS_H
+#define VLC_MPEG_TIMESTAMPS_H
+
+#define FROM_SCALE_NZ(x) ((x) * 100 / 9)
+#define TO_SCALE_NZ(x)   ((x) * 9 / 100)
+
+#define FROM_SCALE(x) (VLC_TS_0 + FROM_SCALE_NZ(x))
+#define TO_SCALE(x)   TO_SCALE_NZ((x) - VLC_TS_0)
+
+static inline int64_t TimeStampWrapAround( int64_t i_first_pcr, int64_t i_time )
+{
+    int64_t i_adjust = 0;
+    if( i_first_pcr > 0x0FFFFFFFF && i_time < 0x0FFFFFFFF )
+        i_adjust = 0x1FFFFFFFF;
+
+    return i_time + i_adjust;
+}
+
+#endif
diff --git a/modules/demux/mpeg/ts.c b/modules/demux/mpeg/ts.c
index fdd43ad55971..d33cf2a531b6 100644
--- a/modules/demux/mpeg/ts.c
+++ b/modules/demux/mpeg/ts.c
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * ts.c: Transport Stream input module for VLC.
  *****************************************************************************
- * Copyright (C) 2004-2005 VLC authors and VideoLAN
+ * Copyright (C) 2004-2016 VLC authors and VideoLAN
  * $Id$
  *
  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
@@ -32,63 +32,40 @@
 
 #include <vlc_common.h>
 #include <vlc_plugin.h>
-
-#include <assert.h>
-#include <time.h>
-
 #include <vlc_access.h>    /* DVB-specific things */
 #include <vlc_demux.h>
-#include <vlc_meta.h>
-#include <vlc_epg.h>
-#include <vlc_charset.h>   /* FromCharset, for EIT */
-#include <vlc_bits.h>
-
-#include "../../mux/mpeg/csa.h"
-
-#include "ts.h"
-#include "ts_strings.h"
 
 /* Include dvbpsi headers */
-# include <dvbpsi/dvbpsi.h>
-# include <dvbpsi/demux.h>
+#ifndef _DVBPSI_DVBPSI_H_
+ # include <dvbpsi/dvbpsi.h>
+#endif
 # include <dvbpsi/descriptor.h>
 # include <dvbpsi/pat.h>
 # include <dvbpsi/pmt.h>
-# include <dvbpsi/sdt.h>
-# include <dvbpsi/dr.h>
-# include <dvbpsi/psi.h>
 
-/* EIT support */
-# include <dvbpsi/eit.h>
+#include "ts_pid.h"
+#include "ts_streams.h"
+#include "ts_streams_private.h"
+#include "ts_psi.h"
+#include "ts_psi_eit.h"
 
-/* TDT support */
-# include <dvbpsi/tot.h>
+#include "ts_hotfixes.h"
+#include "ts_sl.h"
+#include "sections.h"
+#include "pes.h"
+#include "timestamps.h"
 
-#include "../../mux/mpeg/dvbpsi_compat.h"
-#include "../../mux/mpeg/streams.h"
-#include "../../mux/mpeg/tsutil.h"
-#include "../../mux/mpeg/tables.h"
+#include "ts.h"
 
-#include "../../codec/opus_header.h"
 #include "../../codec/scte18.h"
-
 #include "../opus.h"
-
-#include "pes.h"
-#include "mpeg4_iod.h"
-#include "sections.h"
+#include "../../mux/mpeg/csa.h"
 
 #ifdef HAVE_ARIBB24
  #include <aribb24/aribb24.h>
- #include <aribb24/decoder.h>
 #endif
 
-typedef enum arib_modes_e
-{
-    ARIBMODE_AUTO = -1,
-    ARIBMODE_DISABLED = 0,
-    ARIBMODE_ENABLED = 1
-} arib_modes_e;
+#include <assert.h>
 
 /*****************************************************************************
  * Module descriptor
@@ -190,301 +167,14 @@ vlc_module_end ()
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
-static const char *const ppsz_teletext_type[] = {
- "",
- N_("Teletext"),
- N_("Teletext subtitles"),
- N_("Teletext: additional information"),
- N_("Teletext: program schedule"),
- N_("Teletext subtitles: hearing impaired")
-};
-
-typedef struct
-{
-    int             i_version;
-    int             i_ts_id;
-    dvbpsi_t       *handle;
-    DECL_ARRAY(ts_pid_t *) programs;
-
-} ts_pat_t;
-
-typedef struct
-{
-    dvbpsi_t       *handle;
-    int             i_version;
-    int             i_number;
-    int             i_pid_pcr;
-    bool            b_selected;
-    /* IOD stuff (mpeg4) */
-    od_descriptor_t *iod;
-    od_descriptors_t od;
-
-    DECL_ARRAY(ts_pid_t *) e_streams;
-
-    struct
-    {
-        mtime_t i_current;
-        mtime_t i_first; // seen <> != -1
-        /* broken PCR handling */
-        mtime_t i_first_dts;
-        mtime_t i_pcroffset;
-        bool    b_disable; /* ignore PCR field, use dts */
-        bool    b_fix_done;
-    } pcr;
-
-    mtime_t i_last_dts;
-
-} ts_pmt_t;
-
-typedef struct ts_pes_es_t ts_pes_es_t;
-
-struct ts_pes_es_t
-{
-    ts_pmt_t *p_program;
-    es_format_t  fmt;
-    es_out_id_t *id;
-    uint16_t i_sl_es_id;
-    ts_pes_es_t *p_extraes; /* Some private streams encapsulate several ES (eg. DVB subtitles) */
-    ts_pes_es_t *p_next; /* Next es on same pid from different pmt (shared pid) */
-    /* J2K stuff */
-    uint8_t  b_interlaced;
-};
-
-typedef enum
-{
-    TS_ES_DATA_PES,
-    TS_ES_DATA_TABLE_SECTION
-} ts_es_data_type_t;
-
-typedef struct
-{
-    ts_pes_es_t *p_es;
-
-    uint8_t i_stream_type;
-
-    ts_es_data_type_t data_type;
-    int         i_data_size;
-    int         i_data_gathered;
-    block_t     *p_data;
-    block_t     **pp_last;
-    bool        b_always_receive;
-    ts_sections_processor_t *p_sections_proc;
-
-    block_t *   p_prepcr_outqueue;
-
-    /* SL AU */
-    struct
-    {
-        block_t     *p_data;
-        block_t     **pp_last;
-    } sl;
-} ts_pes_t;
-
-
-typedef struct
-{
-    /* for special PAT/SDT case */
-    dvbpsi_t       *handle; /* PAT/SDT/EIT */
-    int             i_version;
-
-} ts_psi_t;
-
-typedef enum
-{
-    TS_PMT_REGISTRATION_NONE = 0,
-    TS_PMT_REGISTRATION_BLURAY,
-    TS_PMT_REGISTRATION_ATSC,
-    TS_PMT_REGISTRATION_ARIB,
-} ts_pmt_registration_type_t;
-
-typedef enum
-{
-    TYPE_FREE = 0,
-    TYPE_PAT,
-    TYPE_PMT,
-    TYPE_PES,
-    TYPE_SDT,
-    TYPE_TDT,
-    TYPE_EIT,
-} ts_pid_type_t;
-
-enum
-{
-    FLAGS_NONE = 0,
-    FLAG_SEEN  = 1,
-    FLAG_SCRAMBLED = 2,
-    FLAG_FILTERED = 4
-};
-
-#define SEEN(x) ((x)->i_flags & FLAG_SEEN)
-#define SCRAMBLED(x) ((x).i_flags & FLAG_SCRAMBLED)
-
-struct ts_pid_t
-{
-    uint16_t    i_pid;
-
-    uint8_t     i_flags;
-    uint8_t     i_cc;   /* countinuity counter */
-    uint8_t     type;
-
-    uint16_t    i_refcount;
-
-    /* */
-    union
-    {
-        ts_pat_t    *p_pat;
-        ts_pmt_t    *p_pmt;
-        ts_pes_t    *p_pes;
-        ts_psi_t    *p_psi;
-    } u;
-
-    struct
-    {
-        vlc_fourcc_t i_fourcc;
-        int i_type;
-        int i_pcr_count;
-    } probed;
-
-};
-
-typedef struct
-{
-    int i_service;
-} vdr_info_t;
-
-#define MIN_ES_PID 4    /* Should be 32.. broken muxers */
-#define MAX_ES_PID 8190
-#define MIN_PAT_INTERVAL CLOCK_FREQ // DVB is 500ms
-
-#define PID_ALLOC_CHUNK 16
-
-struct demux_sys_t
-{
-    stream_t   *stream;
-    bool        b_canseek;
-    bool        b_canfastseek;
-    vlc_mutex_t     csa_lock;
-
-    /* TS packet size (188, 192, 204) */
-    unsigned    i_packet_size;
-
-    /* Additional TS packet header size (BluRay TS packets have 4-byte header before sync byte) */
-    unsigned    i_packet_header_size;
-
-    /* how many TS packet we read at once */
-    unsigned    i_ts_read;
-
-    bool        b_force_seek_per_percent;
-
-    bool        b_atsc_eas;
-    struct
-    {
-        arib_modes_e e_mode;
-#ifdef HAVE_ARIBB24
-        arib_instance_t *p_instance;
-#endif
-        stream_t     *b25stream;
-    } arib;
-
-    /* All pid */
-    struct
-    {
-        ts_pid_t pat;
-        ts_pid_t dummy;
-        /* all non commons ones, dynamically allocated */
-        ts_pid_t **pp_all;
-        int        i_all;
-        int        i_all_alloc;
-        /* last recently used */
-        uint16_t   i_last_pid;
-        ts_pid_t  *p_last;
-    } pids;
-
-    bool        b_user_pmt;
-    int         i_pmt_es;
-    bool        b_es_all; /* If we need to return all es/programs */
-
-    enum
-    {
-        NO_ES, /* for preparse */
-        DELAY_ES,
-        CREATE_ES
-    } es_creation;
-    #define PREPARSING p_sys->es_creation == NO_ES
-
-    /* */
-    bool        b_es_id_pid;
-    uint16_t    i_next_extraid;
-
-    csa_t       *csa;
-    int         i_csa_pkt_size;
-    bool        b_split_es;
-
-    bool        b_trust_pcr;
-
-    /* */
-    bool        b_access_control;
-    bool        b_end_preparse;
-
-    /* */
-    bool        b_dvb_meta;
-    int64_t     i_tdt_delta;
-    int64_t     i_dvb_start;
-    int64_t     i_dvb_length;
-    bool        b_broken_charset; /* True if broken encoding is used in EPG/SDT */
-
-    /* Selected programs */
-    DECL_ARRAY( int ) programs; /* List of selected/access-filtered programs */
-    bool        b_default_selection; /* True if set by default to first pmt seen (to get data from filtered access) */
-
-    struct
-    {
-        mtime_t i_first_dts;     /* first dts encountered for the stream */
-        int     i_timesourcepid; /* which pid we saved the dts from */
-        enum { PAT_WAITING = 0, PAT_MISSING, PAT_FIXTRIED } status; /* set if we haven't seen PAT within MIN_PAT_INTERVAL */
-    } patfix;
-
-    vdr_info_t  vdr;
-
-    /* */
-    bool        b_start_record;
-};
-
 static int Demux    ( demux_t *p_demux );
 static int Control( demux_t *p_demux, int i_query, va_list args );
 
-static void PIDFillFormat( ts_pes_t *p_pes, int i_stream_type, ts_es_data_type_t * );
-
-static bool PIDSetup( demux_t *p_demux, ts_pid_type_t i_type, ts_pid_t *pid, ts_pid_t *p_parent );
-static void PIDRelease( demux_t *p_demux, ts_pid_t *pid );
-
-static void PATCallBack( void*, dvbpsi_pat_t * );
-static void PMTCallBack( void *data, dvbpsi_pmt_t *p_pmt );
-static void PSINewTableCallBack( dvbpsi_t *handle, uint8_t  i_table_id,
-                                 uint16_t i_extension, demux_t * );
-
 static int ChangeKeyCallback( vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void * );
 
-/* Structs */
-static ts_pat_t *ts_pat_New( demux_t * );
-static void ts_pat_Del( demux_t *, ts_pat_t * );
-static ts_pmt_t *ts_pmt_New( demux_t * );
-static void ts_pmt_Del( demux_t *, ts_pmt_t * );
-static ts_pes_es_t * ts_pes_es_New( ts_pmt_t * );
-static void ts_pes_Add_es( ts_pes_t *, ts_pes_es_t *, bool );
-static ts_pes_es_t * ts_pes_Extract_es( ts_pes_t *, const ts_pmt_t * );
-static ts_pes_es_t * ts_pes_Find_es( ts_pes_t *, const ts_pmt_t * );
-static size_t ts_pes_Count_es( const ts_pes_es_t *, bool, const ts_pmt_t * );
-static ts_pes_t *ts_pes_New( demux_t *, ts_pmt_t * );
-static void ts_pes_Del( demux_t *, ts_pes_t * );
-static ts_psi_t *ts_psi_New( demux_t * );
-static void ts_psi_Del( demux_t *, ts_psi_t * );
-
 /* Helpers */
-static ts_pid_t *GetPID( demux_sys_t *, uint16_t i_pid );
 static bool PIDReferencedByProgram( const ts_pmt_t *, uint16_t );
-static bool ProgramIsSelected( demux_sys_t *, uint16_t i_pgrm );
-static void UpdatePESFilters( demux_t *p_demux, bool b_all );
+void UpdatePESFilters( demux_t *p_demux, bool b_all );
 static inline void FlushESBuffer( ts_pes_t *p_pes );
 static void UpdatePIDScrambledState( demux_t *p_demux, ts_pid_t *p_pid, bool );
 static inline int PIDGet( block_t *p )
@@ -493,29 +183,13 @@ static inline int PIDGet( block_t *p )
 }
 
 static bool GatherData( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk );
-static void AddAndCreateES( demux_t *p_demux, ts_pid_t *pid, bool );
 static void ProgramSetPCR( demux_t *p_demux, ts_pmt_t *p_prg, mtime_t i_pcr );
 
 static block_t* ReadTSPacket( demux_t *p_demux );
-static int ProbeStart( demux_t *p_demux, int i_program );
-static int ProbeEnd( demux_t *p_demux, int i_program );
 static int SeekToTime( demux_t *p_demux, const ts_pmt_t *, int64_t time );
 static void ReadyQueuesPostSeek( demux_t *p_demux );
 static void PCRHandle( demux_t *p_demux, ts_pid_t *, block_t * );
 static void PCRFixHandle( demux_t *, ts_pmt_t *, block_t * );
-static int64_t TimeStampWrapAround( const ts_pmt_t *, int64_t );
-
-/* MPEG4 related */
-static const es_mpeg4_descriptor_t * GetMPEG4DescByEsId( const ts_pmt_t *, uint16_t );
-static ts_pes_es_t * GetPMTESBySLEsId( ts_pmt_t *, uint16_t );
-static bool SetupISO14496LogicalStream( demux_t *, const decoder_config_descriptor_t *,
-                                        es_format_t * );
-
-#define TS_USER_PMT_NUMBER (0)
-static int UserPmt( demux_t *p_demux, const char * );
-
-static int UpdateHWFilter( demux_sys_t *, ts_pid_t * );
-static int SetPIDFilter( demux_sys_t *, ts_pid_t *, bool b_selected );
 
 #define TS_PACKET_SIZE_188 188
 #define TS_PACKET_SIZE_192 192
@@ -670,315 +344,15 @@ static int DetectPVRHeadersAndHeaderSize( demux_t *p_demux, unsigned *pi_header_
     return DetectPacketSize( p_demux, pi_header_size, 0 );
 }
 
-static void ProbePES( demux_t *p_demux, ts_pid_t *pid, const uint8_t *p_pesstart, size_t i_data, bool b_adaptfield )
-{
-    demux_sys_t *p_sys = p_demux->p_sys;
-    const uint8_t *p_pes = p_pesstart;
-    pid->probed.i_type = -1;
-
-    if( b_adaptfield )
-    {
-        if ( i_data < 2 )
-            return;
-
-        uint8_t len = *p_pes;
-        p_pes++; i_data--;
-
-        if(len == 0)
-        {
-            p_pes++; i_data--;/* stuffing */
-        }
-        else
-        {
-            if( i_data < len )
-                return;
-            if( len >= 7 && (p_pes[1] & 0x10) )
-                pid->probed.i_pcr_count++;
-            p_pes += len;
-            i_data -= len;
-        }
-    }
-
-    if( i_data < 9 )
-        return;
-
-    if( p_pes[0] != 0 || p_pes[1] != 0 || p_pes[2] != 1 )
-        return;
-
-    size_t i_pesextoffset = 8;
-    mtime_t i_dts = -1;
-    if( p_pes[7] & 0x80 ) // PTS
-    {
-        i_pesextoffset += 5;
-        if ( i_data < i_pesextoffset )
-            return;
-        i_dts = ExtractPESTimestamp( &p_pes[9] );
-    }
-    if( p_pes[7] & 0x40 ) // DTS
-    {
-        i_pesextoffset += 5;
-        if ( i_data < i_pesextoffset )
-            return;
-        i_dts = ExtractPESTimestamp( &p_pes[14] );
-    }
-    if( p_pes[7] & 0x20 ) // ESCR
-        i_pesextoffset += 6;
-    if( p_pes[7] & 0x10 ) // ESrate
-        i_pesextoffset += 3;
-    if( p_pes[7] & 0x08 ) // DSM
-        i_pesextoffset += 1;
-    if( p_pes[7] & 0x04 ) // CopyInfo
-        i_pesextoffset += 1;
-    if( p_pes[7] & 0x02 ) // PESCRC
-        i_pesextoffset += 2;
-
-    if ( i_data < i_pesextoffset )
-        return;
-
-     /* HeaderdataLength */
-    const size_t i_payloadoffset = 8 + 1 + p_pes[8];
-    i_pesextoffset += 1;
-
-    if ( i_data < i_pesextoffset || i_data < i_payloadoffset )
-        return;
-
-    i_data -= 8 + 1 + p_pes[8];
-
-    if( p_pes[7] & 0x01 ) // PESExt
-    {
-        size_t i_extension2_offset = 1;
-        if ( p_pes[i_pesextoffset] & 0x80 ) // private data
-            i_extension2_offset += 16;
-        if ( p_pes[i_pesextoffset] & 0x40 ) // pack
-            i_extension2_offset += 1;
-        if ( p_pes[i_pesextoffset] & 0x20 ) // seq
-            i_extension2_offset += 2;
-        if ( p_pes[i_pesextoffset] & 0x10 ) // P-STD
-            i_extension2_offset += 2;
-        if ( p_pes[i_pesextoffset] & 0x01 ) // Extension 2
-        {
-            uint8_t i_len = p_pes[i_pesextoffset + i_extension2_offset] & 0x7F;
-            i_extension2_offset += i_len;
-        }
-        if( i_data < i_extension2_offset )
-            return;
-
-        i_data -= i_extension2_offset;
-    }
-    /* (i_payloadoffset - i_pesextoffset) 0xFF stuffing */
-
-    if ( i_data < 4 )
-        return;
-
-    const uint8_t *p_data = &p_pes[i_payloadoffset];
-    /* NON MPEG audio & subpictures STREAM */
-    if(p_pes[3] == 0xBD)
-    {
-        if( !memcmp( p_data, "\x7F\xFE\x80\x01", 4 ) )
-        {
-            pid->probed.i_type = 0x06;
-            pid->probed.i_fourcc = VLC_CODEC_DTS;
-        }
-        else if( !memcmp( p_data, "\x0B\x77", 2 ) )
-        {
-            pid->probed.i_type = 0x06;
-            pid->probed.i_fourcc = VLC_CODEC_EAC3;
-        }
-    }
-    /* MPEG AUDIO STREAM */
-    else if(p_pes[3] >= 0xC0 && p_pes[3] <= 0xDF)
-    {
-        if( p_data[0] == 0xFF && (p_data[1] & 0xE0) == 0xE0 )
-        {
-            switch(p_data[1] & 18)
-            {
-            /* 10 - MPEG Version 2 (ISO/IEC 13818-3)
-               11 - MPEG Version 1 (ISO/IEC 11172-3) */
-                case 0x10:
-                    pid->probed.i_type = 0x04;
-                    break;
-                case 0x18:
-                    pid->probed.i_type = 0x03;
-                default:
-                    break;
-            }
-
-            switch(p_data[1] & 6)
-            {
-            /* 01 - Layer III
-               10 - Layer II
-               11 - Layer I */
-                case 0x06:
-                    pid->probed.i_type = 0x04;
-                    pid->probed.i_fourcc = VLC_CODEC_MPGA;
-                    break;
-                case 0x04:
-                    pid->probed.i_type = 0x04;
-                    pid->probed.i_fourcc = VLC_CODEC_MP2;
-                    break;
-                case 0x02:
-                    pid->probed.i_type = 0x04;
-                    pid->probed.i_fourcc = VLC_CODEC_MP3;
-                default:
-                    break;
-            }
-        }
-    }
-    /* VIDEO STREAM */
-    else if( p_pes[3] >= 0xE0 && p_pes[3] <= 0xEF )
-    {
-        if( !memcmp( p_data, "\x00\x00\x00\x01", 4 ) )
-        {
-            pid->probed.i_type = 0x1b;
-            pid->probed.i_fourcc = VLC_CODEC_H264;
-        }
-        else if( !memcmp( p_data, "\x00\x00\x01", 4 ) )
-        {
-            pid->probed.i_type = 0x02;
-            pid->probed.i_fourcc = VLC_CODEC_MPGV;
-        }
-    }
-
-    /* Track timestamps and flag missing PAT */
-    if( !p_sys->patfix.i_timesourcepid && i_dts > -1 )
-    {
-        p_sys->patfix.i_first_dts = i_dts;
-        p_sys->patfix.i_timesourcepid = pid->i_pid;
-    }
-    else if( p_sys->patfix.i_timesourcepid == pid->i_pid && i_dts > -1 &&
-             p_sys->patfix.status == PAT_WAITING )
-    {
-        if( i_dts - p_sys->patfix.i_first_dts > TO_SCALE(MIN_PAT_INTERVAL) )
-            p_sys->patfix.status = PAT_MISSING;
-    }
-
-}
-
-static void BuildPATCallback( void *p_opaque, block_t *p_block )
-{
-    ts_pid_t *pat_pid = (ts_pid_t *) p_opaque;
-    dvbpsi_packet_push( pat_pid->u.p_pat->handle, p_block->p_buffer );
-}
-
-static void BuildPMTCallback( void *p_opaque, block_t *p_block )
-{
-    ts_pid_t *program_pid = (ts_pid_t *) p_opaque;
-    assert(program_pid->type == TYPE_PMT);
-    while( p_block )
-    {
-        dvbpsi_packet_push( program_pid->u.p_pmt->handle,
-                            p_block->p_buffer );
-        p_block = p_block->p_next;
-    }
-}
-
-static void MissingPATPMTFixup( demux_t *p_demux )
-{
-    demux_sys_t *p_sys = p_demux->p_sys;
-    int i_program_number = 1234;
-    int i_program_pid = 1337;
-    int i_pcr_pid = 0x1FFF;
-    int i_num_pes = 0;
-
-    ts_pid_t *p_program_pid = GetPID( p_sys, i_program_pid );
-    if( SEEN(p_program_pid) )
-    {
-        /* Find a free one */
-        for( i_program_pid = MIN_ES_PID;
-             i_program_pid <= MAX_ES_PID && SEEN(p_program_pid);
-             i_program_pid++ )
-        {
-            p_program_pid = GetPID( p_sys, i_program_pid );
-        }
-    }
-
-    for( int i=0; i<p_sys->pids.i_all; i++ )
-    {
-        const ts_pid_t *p_pid = p_sys->pids.pp_all[i];
-        if( !SEEN(p_pid) ||
-            p_pid->probed.i_type == -1 )
-            continue;
-
-        if( i_pcr_pid == 0x1FFF && ( p_pid->probed.i_type == 0x03 ||
-                                     p_pid->probed.i_pcr_count ) )
-            i_pcr_pid = p_pid->i_pid;
-
-        i_num_pes++;
-    }
-
-    if( i_num_pes == 0 )
-        return;
-
-    ts_stream_t patstream =
-    {
-        .i_pid = 0,
-        .i_continuity_counter = 0x10,
-        .b_discontinuity = false
-    };
-
-    ts_stream_t pmtprogramstream =
-    {
-        .i_pid = i_program_pid,
-        .i_continuity_counter = 0x0,
-        .b_discontinuity = false
-    };
-
-    BuildPAT( GetPID(p_sys, 0)->u.p_pat->handle,
-            &p_sys->pids.pat, BuildPATCallback,
-            0, 1,
-            &patstream,
-            1, &pmtprogramstream, &i_program_number );
-
-    /* PAT callback should have been triggered */
-    if( p_program_pid->type != TYPE_PMT )
-    {
-        msg_Err( p_demux, "PAT creation failed" );
-        return;
-    }
-
-    struct esstreams_t
-    {
-        pes_stream_t pes;
-        ts_stream_t ts;
-    };
-    es_format_t esfmt = {0};
-    struct esstreams_t *esstreams = calloc( i_num_pes, sizeof(struct esstreams_t) );
-    pes_mapped_stream_t *mapped = calloc( i_num_pes, sizeof(pes_mapped_stream_t) );
-    if( esstreams && mapped )
-    {
-        int j=0;
-        for( int i=0; i<p_sys->pids.i_all; i++ )
-        {
-            const ts_pid_t *p_pid = p_sys->pids.pp_all[i];
-
-            if( !SEEN(p_pid) ||
-                p_pid->probed.i_type == -1 )
-                continue;
-
-            esstreams[j].pes.i_codec = p_pid->probed.i_fourcc;
-            esstreams[j].pes.i_stream_type = p_pid->probed.i_type;
-            esstreams[j].ts.i_pid = p_pid->i_pid;
-            mapped[j].pes = &esstreams[j].pes;
-            mapped[j].ts = &esstreams[j].ts;
-            mapped[j].fmt = &esfmt;
-            j++;
-        }
-
-        BuildPMT( GetPID(p_sys, 0)->u.p_pat->handle, VLC_OBJECT(p_demux),
-                p_program_pid, BuildPMTCallback,
-                0, 1,
-                i_pcr_pid,
-                NULL,
-                1, &pmtprogramstream, &i_program_number,
-                i_num_pes, mapped );
-    }
-    free(esstreams);
-    free(mapped);
-}
-
 /*****************************************************************************
  * Open
  *****************************************************************************/
+# define VLC_DVBPSI_DEMUX_TABLE_INIT(table,obj) \
+    do { \
+        if( !AttachDvbpsiNewEITTableHandler( (table)->u.p_psi->handle, obj ) ) \
+            msg_Warn( obj, "Can't dvbpsi_AttachDemux on pid %d", (table)->i_pid );\
+    } while (0)
+
 static int Open( vlc_object_t *p_this )
 {
     demux_t     *p_demux = (demux_t*)p_this;
@@ -1021,8 +395,7 @@ static int Open( vlc_object_t *p_this )
 
     p_sys->b_broken_charset = false;
 
-    p_sys->pids.dummy.i_pid = 8191;
-    p_sys->pids.dummy.i_flags = FLAG_SEEN;
+    ts_pid_list_Init( &p_sys->pids );
 
     p_sys->i_packet_size = i_packet_size;
     p_sys->i_packet_header_size = i_packet_header_size;
@@ -1034,14 +407,6 @@ static int Open( vlc_object_t *p_this )
     p_sys->patfix.i_timesourcepid = 0;
     p_sys->patfix.status = PAT_WAITING;
 
-# define VLC_DVBPSI_DEMUX_TABLE_INIT(table,obj) \
-    do { \
-        if( !dvbpsi_AttachDemux( (table)->u.p_psi->handle, (dvbpsi_demux_new_cb_t)PSINewTableCallBack, (obj) ) ) \
-        { \
-            msg_Warn( obj, "Can't dvbpsi_AttachDemux on pid %d", (table)->i_pid );\
-        } \
-    } while (0)
-
     /* Init PAT handler */
     patpid = GetPID(p_sys, 0);
     if ( !PIDSetup( p_demux, TYPE_PAT, patpid, NULL ) )
@@ -1217,16 +582,7 @@ static void Close( vlc_object_t *p_this )
     vlc_mutex_destroy( &p_sys->csa_lock );
 
     /* Release all non default pids */
-    for( int i = 0; i < p_sys->pids.i_all; i++ )
-    {
-        ts_pid_t *pid = p_sys->pids.pp_all[i];
-#ifndef NDEBUG
-        if( pid->type != TYPE_FREE )
-            msg_Err( p_demux, "PID %d type %d not freed", pid->i_pid, pid->type );
-#endif
-        free( pid );
-    }
-    free( p_sys->pids.pp_all );
+    ts_pid_list_Release( p_demux, &p_sys->pids );
 
     free( p_sys );
 }
@@ -1412,7 +768,7 @@ static inline void HasSelectedES( es_out_t *out, const ts_pes_es_t *p_es,
     }
 }
 
-static void UpdatePESFilters( demux_t *p_demux, bool b_all )
+void UpdatePESFilters( demux_t *p_demux, bool b_all )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
     ts_pat_t *p_pat = GetPID(p_sys, 0)->u.p_pat;
@@ -1534,10 +890,10 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
              p_pmt->pcr.i_first > -1 && p_pmt->i_last_dts > VLC_TS_INVALID &&
              p_pmt->pcr.i_current > -1 )
         {
-            double i_length = TimeStampWrapAround( p_pmt,
+            double i_length = TimeStampWrapAround( p_pmt->pcr.i_first,
                                                    p_pmt->i_last_dts ) - p_pmt->pcr.i_first;
             i_length += p_pmt->pcr.i_pcroffset;
-            double i_pos = TimeStampWrapAround( p_pmt,
+            double i_pos = TimeStampWrapAround( p_pmt->pcr.i_first,
                                                 p_pmt->pcr.i_current ) - p_pmt->pcr.i_first;
             if( i_length > 0 )
             {
@@ -1578,7 +934,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
              p_pmt->pcr.i_first > -1 && p_pmt->i_last_dts > VLC_TS_INVALID &&
              p_pmt->pcr.i_current > -1 )
         {
-            int64_t i_length = TimeStampWrapAround( p_pmt,
+            int64_t i_length = TimeStampWrapAround( p_pmt->pcr.i_first,
                                                    p_pmt->i_last_dts ) - p_pmt->pcr.i_first;
             i64 = p_pmt->pcr.i_first + (int64_t)(i_length * f);
             if( i64 <= p_pmt->i_last_dts )
@@ -1625,7 +981,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
 
         if( p_pmt && p_pmt->pcr.i_current > -1 && p_pmt->pcr.i_first > -1 )
         {
-            int64_t i_pcr = TimeStampWrapAround( p_pmt, p_pmt->pcr.i_current );
+            int64_t i_pcr = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->pcr.i_current );
             *pi64 = FROM_SCALE(i_pcr - p_pmt->pcr.i_first);
             return VLC_SUCCESS;
         }
@@ -1646,7 +1002,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
         {
             int64_t i_start = (p_pmt->pcr.i_first > -1) ? p_pmt->pcr.i_first :
                               TO_SCALE(p_pmt->pcr.i_first_dts);
-            int64_t i_last = TimeStampWrapAround( p_pmt, p_pmt->i_last_dts );
+            int64_t i_last = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->i_last_dts );
             i_last += p_pmt->pcr.i_pcroffset;
             *pi64 = FROM_SCALE(i_last - i_start);
             return VLC_SUCCESS;
@@ -1750,326 +1106,51 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
 /*****************************************************************************
  *
  *****************************************************************************/
-static int UserPmt( demux_t *p_demux, const char *psz_fmt )
+static int16_t read_opus_flag(uint8_t **buf, size_t *len)
 {
-    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 *pmtpid = GetPID(p_sys, i_pid);
-
-    msg_Dbg( p_demux, "user pmt specified (pid=%d,number=%d)", i_pid, i_number );
-    if ( !PIDSetup( p_demux, TYPE_PMT, pmtpid, GetPID(p_sys, 0) ) )
-        goto error;
-
-    /* Dummy PMT */
-    ts_pmt_t *p_pmt = pmtpid->u.p_pmt;
-    p_pmt->i_number   = i_number != 0 ? i_number : TS_USER_PMT_NUMBER;
-    if( !dvbpsi_pmt_attach( p_pmt->handle,
-                            ((i_number != TS_USER_PMT_NUMBER ? i_number : 1)),
-                            PMTCallBack, p_demux ) )
-    {
-        PIDRelease( p_demux, pmtpid );
-        goto error;
-    }
-
-    ARRAY_APPEND( GetPID(p_sys, 0)->u.p_pat->programs, pmtpid );
+    if (*len < 2)
+        return -1;
 
-    psz = strchr( psz, '=' );
-    if( psz )
-        psz++;
-    while( psz && *psz )
-    {
-        char *psz_next = strchr( psz, ',' );
-        int i_pid;
+    int16_t ret = ((*buf)[0] << 8) | (*buf)[1];
 
-        if( psz_next )
-            *psz_next++ = '\0';
+    *len -= 2;
+    *buf += 2;
 
-        i_pid = strtol( psz, &psz, 0 );
-        if( *psz != ':' || i_pid < 2 || i_pid >= 8192 )
-            goto next;
+    if (ret & (3<<13))
+        ret = -1;
 
-        char *psz_opt = &psz[1];
-        if( !strcmp( psz_opt, "pcr" ) )
-        {
-            p_pmt->i_pid_pcr = i_pid;
-        }
-        else if( GetPID(p_sys, i_pid)->type == TYPE_FREE )
-        {
-            ts_pid_t *pid = GetPID(p_sys, i_pid);
+    return ret;
+}
 
-            char *psz_arg = strchr( psz_opt, '=' );
-            if( psz_arg )
-                *psz_arg++ = '\0';
+static block_t *Opus_Parse(demux_t *demux, block_t *block)
+{
+    block_t *out = NULL;
+    block_t **last = NULL;
 
-            if ( !PIDSetup( p_demux, TYPE_PES, pid, pmtpid ) )
-                continue;
+    uint8_t *buf = block->p_buffer;
+    size_t len = block->i_buffer;
 
-            ARRAY_APPEND( p_pmt->e_streams, pid );
+    while (len > 3 && ((buf[0] << 3) | (buf[1] >> 5)) == 0x3ff) {
+        int16_t start_trim = 0, end_trim = 0;
+        int start_trim_flag        = (buf[1] >> 4) & 1;
+        int end_trim_flag          = (buf[1] >> 3) & 1;
+        int control_extension_flag = (buf[1] >> 2) & 1;
 
-            if( p_pmt->i_pid_pcr <= 0 )
-                p_pmt->i_pid_pcr = i_pid;
+        len -= 2;
+        buf += 2;
 
-            es_format_t *fmt = &pid->u.p_pes->p_es->fmt;
+        unsigned au_size = 0;
+        while (len--) {
+            int c = *buf++;
+            au_size += c;
+            if (c != 0xff)
+                break;
+        }
 
-            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;
-
-                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->u.p_pes, i_stream_type, &pid->u.p_pes->data_type );
-            }
-
-            fmt->i_group = i_number;
-            if( p_sys->b_es_id_pid )
-                fmt->i_id = i_pid;
-
-            if( fmt->i_cat != UNKNOWN_ES )
-            {
-                msg_Dbg( p_demux, "  * es pid=%d fcc=%4.4s", i_pid,
-                         (char*)&fmt->i_codec );
-                pid->u.p_pes->p_es->id = es_out_Add( p_demux->out, fmt );
-                p_sys->i_pmt_es++;
-            }
-        }
-
-    next:
-        psz = psz_next;
-    }
-
-    p_sys->b_user_pmt = true;
-    free( psz_dup );
-    return VLC_SUCCESS;
-
-error:
-    free( psz_dup );
-    return VLC_EGENERIC;
-}
-
-static int UpdateHWFilter( demux_sys_t *p_sys, ts_pid_t *p_pid )
-{
-    if( !p_sys->b_access_control )
-        return VLC_EGENERIC;
-
-    return stream_Control( p_sys->stream, STREAM_SET_PRIVATE_ID_STATE,
-                           p_pid->i_pid, !!(p_pid->i_flags & FLAG_FILTERED) );
-}
-
-static int SetPIDFilter( demux_sys_t *p_sys, ts_pid_t *p_pid, bool b_selected )
-{
-    if( b_selected )
-        p_pid->i_flags |= FLAG_FILTERED;
-    else
-        p_pid->i_flags &= ~FLAG_FILTERED;
-
-    return UpdateHWFilter( p_sys, p_pid );
-}
-
-static void PIDReset( ts_pid_t *pid )
-{
-    assert(pid->i_refcount == 0);
-    pid->i_cc       = 0xff;
-    pid->i_flags    &= ~FLAG_SCRAMBLED;
-    pid->type = TYPE_FREE;
-}
-
-static bool PIDSetup( demux_t *p_demux, ts_pid_type_t i_type, ts_pid_t *pid, ts_pid_t *p_parent )
-{
-    if( pid == p_parent || pid->i_pid == 0x1FFF )
-        return false;
-
-    if( pid->i_refcount == 0 )
-    {
-        assert( pid->type == TYPE_FREE );
-        switch( i_type )
-        {
-        case TYPE_FREE: /* nonsense ?*/
-            PIDReset( pid );
-            return true;
-
-        case TYPE_PAT:
-            PIDReset( pid );
-            pid->u.p_pat = ts_pat_New( p_demux );
-            if( !pid->u.p_pat )
-                return false;
-            break;
-
-        case TYPE_PMT:
-            PIDReset( pid );
-            pid->u.p_pmt = ts_pmt_New( p_demux );
-            if( !pid->u.p_pmt )
-                return false;
-            break;
-
-        case TYPE_PES:
-            PIDReset( pid );
-            pid->u.p_pes = ts_pes_New( p_demux, p_parent->u.p_pmt );
-            if( !pid->u.p_pes )
-                return false;
-            break;
-
-        case TYPE_SDT:
-        case TYPE_TDT:
-        case TYPE_EIT:
-            PIDReset( pid );
-            pid->u.p_psi = ts_psi_New( p_demux );
-            if( !pid->u.p_psi )
-                return false;
-            break;
-
-        default:
-            assert(false);
-            break;
-        }
-
-        pid->i_refcount++;
-        pid->type = i_type;
-    }
-    else if( pid->type == i_type && pid->i_refcount < UINT16_MAX )
-    {
-        pid->i_refcount++;
-    }
-    else
-    {
-        if( pid->type != TYPE_FREE )
-            msg_Warn( p_demux, "Tried to redeclare pid %d with another type", pid->i_pid );
-        return false;
-    }
-
-    return true;
-}
-
-static void PIDRelease( demux_t *p_demux, ts_pid_t *pid )
-{
-    if( pid->i_refcount == 0 )
-    {
-        assert( pid->type == TYPE_FREE );
-        return;
-    }
-    else if( pid->i_refcount == 1 )
-    {
-        pid->i_refcount--;
-    }
-    else if( pid->i_refcount > 1 )
-    {
-        assert( pid->type != TYPE_FREE && pid->type != TYPE_PAT );
-        pid->i_refcount--;
-    }
-
-    if( pid->i_refcount == 0 )
-    {
-        switch( pid->type )
-        {
-        default:
-        case TYPE_FREE: /* nonsense ?*/
-            assert( pid->type != TYPE_FREE );
-            break;
-
-        case TYPE_PAT:
-            ts_pat_Del( p_demux, pid->u.p_pat );
-            pid->u.p_pat = NULL;
-            break;
-
-        case TYPE_PMT:
-            ts_pmt_Del( p_demux, pid->u.p_pmt );
-            pid->u.p_pmt = NULL;
-            break;
-
-        case TYPE_PES:
-            ts_pes_Del( p_demux, pid->u.p_pes );
-            pid->u.p_pes = NULL;
-            break;
-
-        case TYPE_SDT:
-        case TYPE_TDT:
-        case TYPE_EIT:
-            ts_psi_Del( p_demux, pid->u.p_psi );
-            pid->u.p_psi = NULL;
-            break;
-
-        }
-
-        SetPIDFilter( p_demux->p_sys, pid, false );
-        PIDReset( pid );
-    }
-}
-
-static int16_t read_opus_flag(uint8_t **buf, size_t *len)
-{
-    if (*len < 2)
-        return -1;
-
-    int16_t ret = ((*buf)[0] << 8) | (*buf)[1];
-
-    *len -= 2;
-    *buf += 2;
-
-    if (ret & (3<<13))
-        ret = -1;
-
-    return ret;
-}
-
-static block_t *Opus_Parse(demux_t *demux, block_t *block)
-{
-    block_t *out = NULL;
-    block_t **last = NULL;
-
-    uint8_t *buf = block->p_buffer;
-    size_t len = block->i_buffer;
-
-    while (len > 3 && ((buf[0] << 3) | (buf[1] >> 5)) == 0x3ff) {
-        int16_t start_trim = 0, end_trim = 0;
-        int start_trim_flag        = (buf[1] >> 4) & 1;
-        int end_trim_flag          = (buf[1] >> 3) & 1;
-        int control_extension_flag = (buf[1] >> 2) & 1;
-
-        len -= 2;
-        buf += 2;
-
-        unsigned au_size = 0;
-        while (len--) {
-            int c = *buf++;
-            au_size += c;
-            if (c != 0xff)
-                break;
-        }
-
-        if (start_trim_flag) {
-            start_trim = read_opus_flag(&buf, &len);
-            if (start_trim < 0) {
-                msg_Err(demux, "Invalid start trimming flag");
+        if (start_trim_flag) {
+            start_trim = read_opus_flag(&buf, &len);
+            if (start_trim < 0) {
+                msg_Err(demux, "Invalid start trimming flag");
             }
         }
         if (end_trim_flag) {
@@ -2195,10 +1276,10 @@ static void ParsePES( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes )
     }
     else
     {
-        if( i_pts != -1 )
-            i_pts = TimeStampWrapAround( p_es->p_program, i_pts );
-        if( i_dts != -1 )
-            i_dts = TimeStampWrapAround( p_es->p_program, i_dts );
+        if( i_pts != -1 && p_es->p_program )
+            i_pts = TimeStampWrapAround( p_es->p_program->pcr.i_first, i_pts );
+        if( i_dts != -1 && p_es->p_program )
+            i_dts = TimeStampWrapAround( p_es->p_program->pcr.i_first, i_dts );
     }
 
     if( p_es->i_sl_es_id )
@@ -2360,7 +1441,7 @@ static void ParsePES( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes )
                     p_pmt->pcr.i_current > VLC_TS_INVALID )
                 {
                     int64_t i_dts27 = (p_block->i_dts - VLC_TS_0) * 9 / 100;
-                    int64_t i_pcr = TimeStampWrapAround( p_pmt, p_pmt->pcr.i_current );
+                    int64_t i_pcr = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->pcr.i_current );
                     if( i_dts27 < i_pcr )
                     {
                         p_pmt->pcr.i_pcroffset = i_pcr - i_dts27 + 80000;
@@ -2478,119 +1559,6 @@ static void ParsePES( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes )
     }
 }
 
-static void SCTE18_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content )
-{
-    assert( pid->u.p_pes->p_es->fmt.i_codec == VLC_CODEC_SCTE_18 );
-    ts_pmt_t *p_pmt = pid->u.p_pes->p_es->p_program;
-    mtime_t i_date = TimeStampWrapAround( p_pmt, p_pmt->pcr.i_current );
-
-    int i_priority = scte18_get_EAS_priority( p_content->p_buffer, p_content->i_buffer );
-    msg_Dbg( p_demux, "Received EAS Alert with priority %d", i_priority );
-    /* We need to extract the truncated pts stored inside the payload */
-    ts_pes_es_t *p_es = pid->u.p_pes->p_es;
-    if( p_es->id )
-    {
-        if( i_priority == EAS_PRIORITY_HIGH || i_priority == EAS_PRIORITY_MAX )
-            es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, p_es->id, true );
-        p_content->i_dts = p_content->i_pts = FROM_SCALE( i_date );
-        es_out_Send( p_demux->out, p_es->id, p_content );
-    }
-    else
-        block_Release( p_content );
-}
-
-static void SCTE27_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content )
-{
-    assert( pid->u.p_pes->p_es->fmt.i_codec == VLC_CODEC_SCTE_27 );
-    ts_pmt_t *p_pmt = pid->u.p_pes->p_es->p_program;
-    mtime_t i_date = p_pmt->pcr.i_current;
-
-    /* We need to extract the truncated pts stored inside the payload */
-    int i_index = 0;
-    size_t i_offset = 4;
-    if( p_content->p_buffer[3] & 0x40 )
-    {
-        i_index = ((p_content->p_buffer[7] & 0x0f) << 8) | /* segment number */
-                p_content->p_buffer[8];
-        i_offset += 5;
-    }
-    if( i_index == 0 && p_content->i_buffer > i_offset + 8 ) /* message body */
-    {
-        bool is_immediate = p_content->p_buffer[i_offset + 3] & 0x40;
-        if( !is_immediate )
-        {
-            mtime_t i_display_in = GetDWBE( &p_content->p_buffer[i_offset + 4] );
-            if( i_display_in < i_date )
-                i_date = i_display_in + (1ll << 32);
-            else
-                i_date = i_display_in;
-        }
-
-    }
-
-    p_content->i_dts = p_content->i_pts = VLC_TS_0 + i_date * 100 / 9;
-    PCRFixHandle( p_demux, p_pmt, p_content );
-
-    if( pid->u.p_pes->p_es->id )
-        es_out_Send( p_demux->out, pid->u.p_pes->p_es->id, p_content );
-    else
-        block_Release( p_content );
-}
-
-/* Object stream SL in table sections */
-static void SLPackets_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content )
-{
-    ts_pmt_t *p_pmt = pid->u.p_pes->p_es->p_program;
-
-    const es_mpeg4_descriptor_t *p_mpeg4desc = GetMPEG4DescByEsId( p_pmt, pid->u.p_pes->p_es->i_sl_es_id );
-    if( p_mpeg4desc && p_mpeg4desc->dec_descr.i_objectTypeIndication == 0x01 &&
-        p_mpeg4desc->dec_descr.i_streamType == 0x01 /* Object */ )
-    {
-        const uint8_t *p_data = p_content->p_buffer;
-        int i_data = p_content->i_buffer;
-
-        od_descriptors_t *p_ods = &p_pmt->od;
-        sl_header_data header = DecodeSLHeader( i_data, p_data, &p_mpeg4desc->sl_descr );
-
-        DecodeODCommand( VLC_OBJECT(p_demux), p_ods, i_data - header.i_size, &p_data[header.i_size] );
-        bool b_changed = false;
-
-        for( int i=0; i<p_ods->objects.i_size; i++ )
-        {
-            od_descriptor_t *p_od = p_ods->objects.p_elems[i];
-            for( int j = 0; j < ES_DESCRIPTOR_COUNT && p_od->es_descr[j].b_ok; j++ )
-            {
-                p_mpeg4desc = &p_od->es_descr[j];
-                ts_pes_es_t *p_es = GetPMTESBySLEsId( p_pmt, p_mpeg4desc->i_es_id );
-                es_format_t fmt;
-                es_format_Init( &fmt, UNKNOWN_ES, 0 );
-                fmt.i_id = p_es->fmt.i_id;
-                fmt.i_group = p_es->fmt.i_group;
-
-                if ( p_mpeg4desc && p_mpeg4desc->b_ok && p_es &&
-                     SetupISO14496LogicalStream( p_demux, &p_mpeg4desc->dec_descr, &fmt ) &&
-                     !es_format_IsSimilar( &fmt, &p_es->fmt ) )
-                {
-                    es_format_Clean( &p_es->fmt );
-                    p_es->fmt = fmt;
-
-                    if( p_es->id )
-                        es_out_Del( p_demux->out, p_es->id );
-                    p_es->fmt.b_packetized = true; /* Split by access unit, no sync code */
-                    FREENULL( p_es->fmt.psz_description );
-                    p_es->id = es_out_Add( p_demux->out, &p_es->fmt );
-                    b_changed = true;
-                }
-            }
-        }
-
-        if( b_changed )
-            UpdatePESFilters( p_demux, p_demux->p_sys->b_es_all );
-    }
-
-    block_Release( p_content );
-}
-
 static void ParseData( demux_t *p_demux, ts_pid_t *pid )
 {
     block_t *p_datachain = pid->u.p_pes->p_data;
@@ -2700,15 +1668,6 @@ static block_t* ReadTSPacket( demux_t *p_demux )
     return p_pkt;
 }
 
-static int64_t TimeStampWrapAround( const ts_pmt_t *p_pmt, int64_t i_time )
-{
-    int64_t i_adjust = 0;
-    if( p_pmt && p_pmt->pcr.i_first > 0x0FFFFFFFF && i_time < 0x0FFFFFFFF )
-        i_adjust = 0x1FFFFFFFF;
-
-    return i_time + i_adjust;
-}
-
 static mtime_t GetPCR( block_t *p_pkt )
 {
     const uint8_t *p = p_pkt->p_buffer;
@@ -2898,7 +1857,7 @@ static int SeekToTime( demux_t *p_demux, const ts_pmt_t *p_pmt, int64_t i_scaled
 
             if( i_pcr != -1 )
             {
-                int64_t i_diff = i_scaledtime - TimeStampWrapAround( p_pmt, i_pcr );
+                int64_t i_diff = i_scaledtime - TimeStampWrapAround( p_pmt->pcr.i_first, i_pcr );
                 if ( i_diff < 0 )
                     i_tail_pos = i_splitpos - p_sys->i_packet_size;
                 else if( i_diff < TO_SCALE(VLC_TS_0 + CLOCK_FREQ / 2) ) // 500ms
@@ -2922,56 +1881,6 @@ static int SeekToTime( demux_t *p_demux, const ts_pmt_t *p_pmt, int64_t i_scaled
     return VLC_SUCCESS;
 }
 
-static ts_pid_t *GetPID( demux_sys_t *p_sys, uint16_t i_pid )
-{
-    switch( i_pid )
-    {
-        case 0:
-            return &p_sys->pids.pat;
-        case 0x1FFF:
-            return &p_sys->pids.dummy;
-        default:
-            if( p_sys->pids.i_last_pid == i_pid )
-                return p_sys->pids.p_last;
-        break;
-    }
-
-    for( int i=0; i < p_sys->pids.i_all; i++ )
-    {
-        if( p_sys->pids.pp_all[i]->i_pid == i_pid )
-        {
-            p_sys->pids.p_last = p_sys->pids.pp_all[i];
-            p_sys->pids.i_last_pid = i_pid;
-            return p_sys->pids.p_last;
-        }
-    }
-
-    if( p_sys->pids.i_all >= p_sys->pids.i_all_alloc )
-    {
-        ts_pid_t **p_realloc = realloc( p_sys->pids.pp_all,
-                                       (p_sys->pids.i_all_alloc + PID_ALLOC_CHUNK) * sizeof(ts_pid_t *) );
-        if( !p_realloc )
-            return NULL;
-        p_sys->pids.pp_all = p_realloc;
-        p_sys->pids.i_all_alloc += PID_ALLOC_CHUNK;
-    }
-
-    ts_pid_t *p_pid = calloc( 1, sizeof(*p_pid) );
-    if( !p_pid )
-    {
-        abort();
-        //return NULL;
-    }
-
-    p_pid->i_pid = i_pid;
-    p_sys->pids.pp_all[p_sys->pids.i_all++] = p_pid;
-
-    p_sys->pids.p_last = p_pid;
-    p_sys->pids.i_last_pid = i_pid;
-
-    return p_pid;
-}
-
 #define PROBE_CHUNK_COUNT 250
 
 static int ProbeChunk( demux_t *p_demux, int i_program, bool b_end, int64_t *pi_pcr, bool *pb_found )
@@ -3066,7 +1975,7 @@ static int ProbeChunk( demux_t *p_demux, int i_program, bool b_end, int64_t *pi_
     return i_count;
 }
 
-static int ProbeStart( demux_t *p_demux, int i_program )
+int ProbeStart( demux_t *p_demux, int i_program )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
     const int64_t i_initial_pos = stream_Tell( p_sys->stream );
@@ -3097,7 +2006,7 @@ static int ProbeStart( demux_t *p_demux, int i_program )
     return (b_found) ? VLC_SUCCESS : VLC_EGENERIC;
 }
 
-static int ProbeEnd( demux_t *p_demux, int i_program )
+int ProbeEnd( demux_t *p_demux, int i_program )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
     const int64_t i_initial_pos = stream_Tell( p_sys->stream );
@@ -3236,7 +2145,7 @@ static void PCRHandle( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk )
     for( int i = 0; i < p_pat->programs.i_size; i++ )
     {
         ts_pmt_t *p_pmt = p_pat->programs.p_elems[i]->u.p_pmt;
-        mtime_t i_program_pcr = TimeStampWrapAround( p_pmt, i_pcr );
+        mtime_t i_program_pcr = TimeStampWrapAround( p_pmt->pcr.i_first, i_pcr );
 
         if( p_pmt->i_pid_pcr == 0x1FFF ) /* That program has no dedicated PCR pid ISO/IEC 13818-1 2.4.4.9 */
         {
@@ -3260,7 +2169,7 @@ static void PCRHandle( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk )
     }
 }
 
-static int FindPCRCandidate( ts_pmt_t *p_pmt )
+int FindPCRCandidate( ts_pmt_t *p_pmt )
 {
     ts_pid_t *p_cand = NULL;
     int i_previous = p_pmt->i_pid_pcr;
@@ -3359,7 +2268,7 @@ static bool GatherData( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk )
             pid->u.p_pes->p_data->i_flags |= BLOCK_FLAG_CORRUPTED;
     }
 
-    if( p_demux->p_sys->csa )
+    if( p_demux->p_sys->csa && SCRAMBLED(*pid) )
     {
         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 );
@@ -3513,91 +2422,12 @@ static bool GatherData( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk )
     return i_ret;
 }
 
-static void PIDFillFormat( ts_pes_t *p_pes, int i_stream_type, ts_es_data_type_t *p_datatype )
-{
-    es_format_t *fmt = &p_pes->p_es->fmt;
-    switch( i_stream_type )
-    {
-    case 0x01:  /* MPEG-1 video */
-    case 0x02:  /* MPEG-2 video */
-    case 0x80:  /* MPEG-2 MOTO video */
-        es_format_Init( fmt, VIDEO_ES, VLC_CODEC_MPGV );
-        break;
-    case 0x03:  /* MPEG-1 audio */
-    case 0x04:  /* MPEG-2 audio */
-        es_format_Init( fmt, AUDIO_ES, VLC_CODEC_MPGA );
-        break;
-    case 0x11:  /* MPEG4 (audio) LATM */
-    case 0x0f:  /* ISO/IEC 13818-7 Audio with ADTS transport syntax */
-    case 0x1c:  /* ISO/IEC 14496-3 Audio, without using any additional
-                   transport syntax, such as DST, ALS and SLS */
-        es_format_Init( fmt, AUDIO_ES, VLC_CODEC_MP4A );
-        break;
-    case 0x10:  /* MPEG4 (video) */
-        es_format_Init( fmt, VIDEO_ES, VLC_CODEC_MP4V );
-        break;
-    case 0x1B:  /* H264 <- check transport syntax/needed descriptor */
-        es_format_Init( fmt, VIDEO_ES, VLC_CODEC_H264 );
-        break;
-    case 0x24:  /* HEVC */
-        es_format_Init( fmt, VIDEO_ES, VLC_CODEC_HEVC );
-        break;
-    case 0x42:  /* CAVS (Chinese AVS) */
-        es_format_Init( fmt, VIDEO_ES, VLC_CODEC_CAVS );
-        break;
-
-    case 0x81:  /* A52 (audio) */
-        es_format_Init( fmt, AUDIO_ES, VLC_CODEC_A52 );
-        break;
-    case 0x82:  /* SCTE-27 (sub) */
-        es_format_Init( fmt, SPU_ES, VLC_CODEC_SCTE_27 );
-        *p_datatype = TS_ES_DATA_TABLE_SECTION;
-        ts_sections_processor_Add( &p_pes->p_sections_proc, 0xC6, 0x82,
-                                   true, SCTE27_Section_Handler );
-        break;
-    case 0x84:  /* SDDS (audio) */
-        es_format_Init( fmt, AUDIO_ES, VLC_CODEC_SDDS );
-        break;
-    case 0x85:  /* DTS (audio) */
-        es_format_Init( fmt, AUDIO_ES, VLC_CODEC_DTS );
-        break;
-    case 0x87: /* E-AC3 */
-        es_format_Init( fmt, AUDIO_ES, VLC_CODEC_EAC3 );
-        break;
-
-    case 0x91:  /* A52 vls (audio) */
-        es_format_Init( fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', 'b' ) );
-        break;
-    case 0x92:  /* DVD_SPU vls (sub) */
-        es_format_Init( fmt, SPU_ES, VLC_FOURCC( 's', 'p', 'u', 'b' ) );
-        break;
-
-    case 0x94:  /* SDDS (audio) */
-        es_format_Init( fmt, AUDIO_ES, VLC_FOURCC( 's', 'd', 'd', 'b' ) );
-        break;
-
-    case 0xa0:  /* MSCODEC vlc (video) (fixed later) */
-        es_format_Init( fmt, UNKNOWN_ES, 0 );
-        break;
-
-    case 0x06:  /* PES_PRIVATE  (fixed later) */
-    case 0x12:  /* MPEG-4 generic (sub/scene/...) (fixed later) */
-    case 0xEA:  /* Privately managed ES (VC-1) (fixed later */
-    default:
-        es_format_Init( fmt, UNKNOWN_ES, 0 );
-        break;
-    }
-
-    /* PES packets usually contain truncated frames */
-    fmt->b_packetized = false;
-}
-
 /****************************************************************************
  ****************************************************************************
  ** libdvbpsi callbacks
  ****************************************************************************
  ****************************************************************************/
-static bool ProgramIsSelected( demux_sys_t *p_sys, uint16_t i_pgrm )
+bool ProgramIsSelected( demux_sys_t *p_sys, uint16_t i_pgrm )
 {
     for(int i=0; i<p_sys->programs.i_size; i++)
         if( p_sys->programs.p_elems[i] == i_pgrm )
@@ -3615,2447 +2445,60 @@ static bool PIDReferencedByProgram( const ts_pmt_t *p_pmt, uint16_t i_pid )
     return false;
 }
 
-static void ValidateDVBMeta( demux_t *p_demux, int i_pid )
-{
-    demux_sys_t *p_sys = p_demux->p_sys;
-
-    if( !p_sys->b_dvb_meta || ( i_pid != 0x11 && i_pid != 0x12 && i_pid != 0x14 ) )
-        return;
-
-    msg_Warn( p_demux, "Switching to non DVB mode" );
-
-    /* This doesn't look like a DVB stream so don't try
-     * parsing the SDT/EDT/TDT */
-
-    PIDRelease( p_demux, GetPID(p_sys, 0x11) );
-    PIDRelease( p_demux, GetPID(p_sys, 0x12) );
-    PIDRelease( p_demux, GetPID(p_sys, 0x14) );
-    p_sys->b_dvb_meta = false;
-}
-
-#include "../dvb-text.h"
-
-static char *EITConvertToUTF8( demux_t *p_demux,
-                               const unsigned char *psz_instring,
-                               size_t i_length,
-                               bool b_broken )
+static void DoCreateES( demux_t *p_demux, ts_pes_es_t *p_es, const ts_pes_es_t *p_parent_es )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
-#ifdef HAVE_ARIBB24
-    if( p_sys->arib.e_mode == ARIBMODE_ENABLED )
+    for( ; p_es ; p_es = p_es->p_next )
     {
-        if ( !p_sys->arib.p_instance )
-            p_sys->arib.p_instance = arib_instance_new( p_demux );
-        if ( !p_sys->arib.p_instance )
-            return NULL;
-        arib_decoder_t *p_decoder = arib_get_decoder( p_sys->arib.p_instance );
-        if ( !p_decoder )
-            return NULL;
-
-        char *psz_outstring = NULL;
-        size_t i_out;
-
-        i_out = i_length * 4;
-        psz_outstring = (char*) calloc( i_out + 1, sizeof(char) );
-        if( !psz_outstring )
-            return NULL;
-
-        arib_initialize_decoder( p_decoder );
-        i_out = arib_decode_buffer( p_decoder, psz_instring, i_length,
-                                    psz_outstring, i_out );
-        arib_finalize_decoder( p_decoder );
-
-        return psz_outstring;
-    }
-#else
-    VLC_UNUSED(p_sys);
-#endif
-    /* Deal with no longer broken providers (no switch byte
-      but sending ISO_8859-1 instead of ISO_6937) without
-      removing them from the broken providers table
-      (keep the entry for correctly handling recorded TS).
-    */
-    b_broken = b_broken && i_length && *psz_instring > 0x20;
-
-    if( b_broken )
-        return FromCharset( "ISO_8859-1", psz_instring, i_length );
-    return vlc_from_EIT( psz_instring, i_length );
-}
-
-static void SDTCallBack( demux_t *p_demux, dvbpsi_sdt_t *p_sdt )
-{
-    demux_sys_t          *p_sys = p_demux->p_sys;
-    ts_pid_t             *sdt = GetPID(p_sys, 0x11);
-    dvbpsi_sdt_service_t *p_srv;
-
-    msg_Dbg( p_demux, "SDTCallBack called" );
-
-    if( p_sys->es_creation != CREATE_ES ||
-       !p_sdt->b_current_next ||
-        p_sdt->i_version == sdt->u.p_psi->i_version )
-    {
-        dvbpsi_sdt_delete( p_sdt );
-        return;
-    }
-
-    msg_Dbg( p_demux, "new SDT ts_id=%d version=%d current_next=%d "
-             "network_id=%d",
-             p_sdt->i_extension,
-             p_sdt->i_version, p_sdt->b_current_next,
-             p_sdt->i_network_id );
-
-    p_sys->b_broken_charset = false;
-
-    for( p_srv = p_sdt->p_first_service; p_srv; p_srv = p_srv->p_next )
-    {
-        vlc_meta_t          *p_meta;
-        dvbpsi_descriptor_t *p_dr;
-
-        const char *psz_type = NULL;
-        const char *psz_status = NULL;
-
-        msg_Dbg( p_demux, "  * service id=%d eit schedule=%d present=%d "
-                 "running=%d free_ca=%d",
-                 p_srv->i_service_id, p_srv->b_eit_schedule,
-                 p_srv->b_eit_present, p_srv->i_running_status,
-                 p_srv->b_free_ca );
-
-        if( p_sys->vdr.i_service && p_srv->i_service_id != p_sys->vdr.i_service )
-        {
-            msg_Dbg( p_demux, "  * service id=%d skipped (not declared in vdr header)",
-                     p_sys->vdr.i_service );
-            continue;
-        }
-
-        p_meta = vlc_meta_New();
-        for( p_dr = p_srv->p_first_descriptor; p_dr; p_dr = p_dr->p_next )
-        {
-            if( p_dr->i_tag == 0x48 )
-            {
-                static const char *ppsz_type[17] = {
-                    "Reserved",
-                    "Digital television service",
-                    "Digital radio sound service",
-                    "Teletext service",
-                    "NVOD reference service",
-                    "NVOD time-shifted service",
-                    "Mosaic service",
-                    "PAL coded signal",
-                    "SECAM coded signal",
-                    "D/D2-MAC",
-                    "FM Radio",
-                    "NTSC coded signal",
-                    "Data broadcast service",
-                    "Reserved for Common Interface Usage",
-                    "RCS Map (see EN 301 790 [35])",
-                    "RCS FLS (see EN 301 790 [35])",
-                    "DVB MHP service"
-                };
-                dvbpsi_service_dr_t *pD = dvbpsi_DecodeServiceDr( p_dr );
-                char *str1 = NULL;
-                char *str2 = NULL;
-
-                /* Workarounds for broadcasters with broken EPG */
-
-                if( p_sdt->i_network_id == 133 )
-                    p_sys->b_broken_charset = true;  /* SKY DE & BetaDigital use ISO8859-1 */
-
-                /* List of providers using ISO8859-1 */
-                static const char ppsz_broken_providers[][8] = {
-                    "CSAT",     /* CanalSat FR */
-                    "GR1",      /* France televisions */
-                    "MULTI4",   /* NT1 */
-                    "MR5",      /* France 2/M6 HD */
-                    ""
-                };
-                for( int i = 0; *ppsz_broken_providers[i]; i++ )
-                {
-                    const size_t i_length = strlen(ppsz_broken_providers[i]);
-                    if( pD->i_service_provider_name_length == i_length &&
-                        !strncmp( (char *)pD->i_service_provider_name, ppsz_broken_providers[i], i_length ) )
-                        p_sys->b_broken_charset = true;
-                }
-
-                /* FIXME: Digital+ ES also uses ISO8859-1 */
-
-                str1 = EITConvertToUTF8(p_demux,
-                                        pD->i_service_provider_name,
-                                        pD->i_service_provider_name_length,
-                                        p_sys->b_broken_charset );
-                str2 = EITConvertToUTF8(p_demux,
-                                        pD->i_service_name,
-                                        pD->i_service_name_length,
-                                        p_sys->b_broken_charset );
-
-                msg_Dbg( p_demux, "    - type=%d provider=%s name=%s",
-                         pD->i_service_type, str1, str2 );
-
-                vlc_meta_SetTitle( p_meta, str2 );
-                vlc_meta_SetPublisher( p_meta, str1 );
-                if( pD->i_service_type >= 0x01 && pD->i_service_type <= 0x10 )
-                    psz_type = ppsz_type[pD->i_service_type];
-                free( str1 );
-                free( str2 );
-            }
-        }
-
-        if( p_srv->i_running_status >= 0x01 && p_srv->i_running_status <= 0x04 )
-        {
-            static const char *ppsz_status[5] = {
-                "Unknown",
-                "Not running",
-                "Starts in a few seconds",
-                "Pausing",
-                "Running"
-            };
-            psz_status = ppsz_status[p_srv->i_running_status];
-        }
-
-        if( psz_type )
-            vlc_meta_AddExtra( p_meta, "Type", psz_type );
-        if( psz_status )
-            vlc_meta_AddExtra( p_meta, "Status", psz_status );
-
-        es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META,
-                        p_srv->i_service_id, p_meta );
-        vlc_meta_Delete( p_meta );
-    }
-
-    sdt->u.p_psi->i_version = p_sdt->i_version;
-    dvbpsi_sdt_delete( p_sdt );
-}
-
-static void EITDecodeMjd( int i_mjd, int *p_y, int *p_m, int *p_d )
-{
-    const int yp = (int)( ( (double)i_mjd - 15078.2)/365.25 );
-    const int mp = (int)( ((double)i_mjd - 14956.1 - (int)(yp * 365.25)) / 30.6001 );
-    const int c = ( mp == 14 || mp == 15 ) ? 1 : 0;
-
-    *p_y = 1900 + yp + c*1;
-    *p_m = mp - 1 - c*12;
-    *p_d = i_mjd - 14956 - (int)(yp*365.25) - (int)(mp*30.6001);
-}
-#define CVT_FROM_BCD(v) ((((v) >> 4)&0xf)*10 + ((v)&0xf))
-static int64_t EITConvertStartTime( uint64_t i_date )
-{
-    const int i_mjd = i_date >> 24;
-    struct tm tm;
-
-    tm.tm_hour = CVT_FROM_BCD(i_date >> 16);
-    tm.tm_min  = CVT_FROM_BCD(i_date >>  8);
-    tm.tm_sec  = CVT_FROM_BCD(i_date      );
-
-    /* if all 40 bits are 1, the start is unknown */
-    if( i_date == UINT64_C(0xffffffffff) )
-        return -1;
-
-    EITDecodeMjd( i_mjd, &tm.tm_year, &tm.tm_mon, &tm.tm_mday );
-    tm.tm_year -= 1900;
-    tm.tm_mon--;
-    tm.tm_isdst = 0;
-
-    return timegm( &tm );
-}
-static int EITConvertDuration( uint32_t i_duration )
-{
-    return CVT_FROM_BCD(i_duration >> 16) * 3600 +
-           CVT_FROM_BCD(i_duration >> 8 ) * 60 +
-           CVT_FROM_BCD(i_duration      );
-}
-#undef CVT_FROM_BCD
-
-static void TDTCallBack( demux_t *p_demux, dvbpsi_tot_t *p_tdt )
-{
-    demux_sys_t        *p_sys = p_demux->p_sys;
-
-    p_sys->i_tdt_delta = CLOCK_FREQ * EITConvertStartTime( p_tdt->i_utc_time )
-                         - mdate();
-    dvbpsi_tot_delete(p_tdt);
-}
-
-
-static void EITCallBack( demux_t *p_demux,
-                         dvbpsi_eit_t *p_eit, bool b_current_following )
-{
-    demux_sys_t        *p_sys = p_demux->p_sys;
-    dvbpsi_eit_event_t *p_evt;
-    vlc_epg_t *p_epg;
-
-    msg_Dbg( p_demux, "EITCallBack called" );
-    if( !p_eit->b_current_next )
-    {
-        dvbpsi_eit_delete( p_eit );
-        return;
-    }
-
-    msg_Dbg( p_demux, "new EIT service_id=%d version=%d current_next=%d "
-             "ts_id=%d network_id=%d segment_last_section_number=%d "
-             "last_table_id=%d",
-             p_eit->i_extension,
-             p_eit->i_version, p_eit->b_current_next,
-             p_eit->i_ts_id, p_eit->i_network_id,
-             p_eit->i_segment_last_section_number, p_eit->i_last_table_id );
-
-    p_epg = vlc_epg_New( NULL );
-    for( p_evt = p_eit->p_first_event; p_evt; p_evt = p_evt->p_next )
-    {
-        dvbpsi_descriptor_t *p_dr;
-        char                *psz_name = NULL;
-        char                *psz_text = NULL;
-        char                *psz_extra = strdup("");
-        int64_t i_start;
-        int i_duration;
-        int i_min_age = 0;
-        int64_t i_tot_time = 0;
-
-        i_start = EITConvertStartTime( p_evt->i_start_time );
-        i_duration = EITConvertDuration( p_evt->i_duration );
-
-        if( p_sys->arib.e_mode == ARIBMODE_ENABLED )
-        {
-            if( p_sys->i_tdt_delta == 0 )
-                p_sys->i_tdt_delta = CLOCK_FREQ * (i_start + i_duration - 5) - mdate();
-
-            i_tot_time = (mdate() + p_sys->i_tdt_delta) / CLOCK_FREQ;
-
-            tzset(); // JST -> UTC
-            i_start += timezone; // FIXME: what about DST?
-            i_tot_time += timezone;
-
-            if( p_evt->i_running_status == 0x00 &&
-                (i_start - 5 < i_tot_time &&
-                 i_tot_time < i_start + i_duration + 5) )
-            {
-                p_evt->i_running_status = 0x04;
-                msg_Dbg( p_demux, "  EIT running status 0x00 -> 0x04" );
-            }
-        }
-
-        msg_Dbg( p_demux, "  * event id=%d start_time:%d duration=%d "
-                          "running=%d free_ca=%d",
-                 p_evt->i_event_id, (int)i_start, (int)i_duration,
-                 p_evt->i_running_status, p_evt->b_free_ca );
-
-        for( p_dr = p_evt->p_first_descriptor; p_dr; p_dr = p_dr->p_next )
-        {
-            switch(p_dr->i_tag)
-            {
-            case 0x4d:
-            {
-                dvbpsi_short_event_dr_t *pE = dvbpsi_DecodeShortEventDr( p_dr );
-
-                /* Only take first description, as we don't handle language-info
-                   for epg atm*/
-                if( pE && psz_name == NULL )
-                {
-                    psz_name = EITConvertToUTF8( p_demux,
-                                                 pE->i_event_name, pE->i_event_name_length,
-                                                 p_sys->b_broken_charset );
-                    free( psz_text );
-                    psz_text = EITConvertToUTF8( p_demux,
-                                                 pE->i_text, pE->i_text_length,
-                                                 p_sys->b_broken_charset );
-                    msg_Dbg( p_demux, "    - short event lang=%3.3s '%s' : '%s'",
-                             pE->i_iso_639_code, psz_name, psz_text );
-                }
-            }
-                break;
-
-            case 0x4e:
-            {
-                dvbpsi_extended_event_dr_t *pE = dvbpsi_DecodeExtendedEventDr( p_dr );
-                if( pE )
-                {
-                    msg_Dbg( p_demux, "    - extended event lang=%3.3s [%d/%d]",
-                             pE->i_iso_639_code,
-                             pE->i_descriptor_number, pE->i_last_descriptor_number );
-
-                    if( pE->i_text_length > 0 )
-                    {
-                        char *psz_text = EITConvertToUTF8( p_demux,
-                                                           pE->i_text, pE->i_text_length,
-                                                           p_sys->b_broken_charset );
-                        if( psz_text )
-                        {
-                            msg_Dbg( p_demux, "       - text='%s'", psz_text );
-
-                            psz_extra = xrealloc( psz_extra,
-                                   strlen(psz_extra) + strlen(psz_text) + 1 );
-                            strcat( psz_extra, psz_text );
-                            free( psz_text );
-                        }
-                    }
-
-                    for( int i = 0; i < pE->i_entry_count; i++ )
-                    {
-                        char *psz_dsc = EITConvertToUTF8( p_demux,
-                                                          pE->i_item_description[i],
-                                                          pE->i_item_description_length[i],
-                                                          p_sys->b_broken_charset );
-                        char *psz_itm = EITConvertToUTF8( p_demux,
-                                                          pE->i_item[i], pE->i_item_length[i],
-                                                          p_sys->b_broken_charset );
-
-                        if( psz_dsc && psz_itm )
-                        {
-                            msg_Dbg( p_demux, "       - desc='%s' item='%s'", psz_dsc, psz_itm );
-#if 0
-                            psz_extra = xrealloc( psz_extra,
-                                         strlen(psz_extra) + strlen(psz_dsc) +
-                                         strlen(psz_itm) + 3 + 1 );
-                            strcat( psz_extra, "(" );
-                            strcat( psz_extra, psz_dsc );
-                            strcat( psz_extra, " " );
-                            strcat( psz_extra, psz_itm );
-                            strcat( psz_extra, ")" );
-#endif
-                        }
-                        free( psz_dsc );
-                        free( psz_itm );
-                    }
-                }
-            }
-                break;
-
-            case 0x55:
-            {
-                dvbpsi_parental_rating_dr_t *pR = dvbpsi_DecodeParentalRatingDr( p_dr );
-                if ( pR )
-                {
-                    for ( int i = 0; i < pR->i_ratings_number; i++ )
-                    {
-                        const dvbpsi_parental_rating_t *p_rating = & pR->p_parental_rating[ i ];
-                        if ( p_rating->i_rating > 0x00 && p_rating->i_rating <= 0x0F )
-                        {
-                            if ( p_rating->i_rating + 3 > i_min_age )
-                                i_min_age = p_rating->i_rating + 3;
-                            msg_Dbg( p_demux, "    - parental control set to %d years",
-                                     i_min_age );
-                        }
-                    }
-                }
-            }
-                break;
-
-            default:
-                msg_Dbg( p_demux, "    - event unknown dr 0x%x(%d)", p_dr->i_tag, p_dr->i_tag );
-                break;
-            }
-        }
-
-        /* */
-        if( i_start > 0 && psz_name && psz_text)
-            vlc_epg_AddEvent( p_epg, i_start, i_duration, psz_name, psz_text,
-                              *psz_extra ? psz_extra : NULL, i_min_age );
-
-        /* Update "now playing" field */
-        if( p_evt->i_running_status == 0x04 && i_start > 0  && psz_name && psz_text )
-            vlc_epg_SetCurrent( p_epg, i_start );
-
-        free( psz_name );
-        free( psz_text );
-
-        free( psz_extra );
-    }
-    if( p_epg->i_event > 0 )
-    {
-        if( b_current_following &&
-            (  p_sys->programs.i_size == 0 ||
-               p_sys->programs.p_elems[0] ==
-                    p_eit->i_extension
-                ) )
-        {
-            p_sys->i_dvb_length = 0;
-            p_sys->i_dvb_start = 0;
-
-            if( p_epg->p_current )
-            {
-                p_sys->i_dvb_start = CLOCK_FREQ * p_epg->p_current->i_start;
-                p_sys->i_dvb_length = CLOCK_FREQ * p_epg->p_current->i_duration;
-            }
-        }
-        es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG,
-                        p_eit->i_extension,
-                        p_epg );
-    }
-    vlc_epg_Delete( p_epg );
-
-    dvbpsi_eit_delete( p_eit );
-}
-static void EITCallBackCurrentFollowing( demux_t *p_demux, dvbpsi_eit_t *p_eit )
-{
-    EITCallBack( p_demux, p_eit, true );
-}
-static void EITCallBackSchedule( demux_t *p_demux, dvbpsi_eit_t *p_eit )
-{
-    EITCallBack( p_demux, p_eit, false );
-}
-
-static void PSINewTableCallBack( dvbpsi_t *h, uint8_t i_table_id,
-                                 uint16_t i_extension, demux_t *p_demux )
-{
-    demux_sys_t *p_sys = p_demux->p_sys;
-    assert( h );
-#if 0
-    msg_Dbg( p_demux, "PSINewTableCallBack: table 0x%x(%d) ext=0x%x(%d)",
-             i_table_id, i_table_id, i_extension, i_extension );
-#endif
-    if( GetPID(p_sys, 0)->u.p_pat->i_version != -1 && i_table_id == 0x42 )
-    {
-        msg_Dbg( p_demux, "PSINewTableCallBack: table 0x%x(%d) ext=0x%x(%d)",
-                 i_table_id, i_table_id, i_extension, i_extension );
-
-        if( !dvbpsi_sdt_attach( h, i_table_id, i_extension, (dvbpsi_sdt_callback)SDTCallBack, p_demux ) )
-            msg_Err( p_demux, "PSINewTableCallback: failed attaching SDTCallback" );
-    }
-    else if( GetPID(p_sys, 0x11)->u.p_psi->i_version != -1 &&
-             ( i_table_id == 0x4e || /* Current/Following */
-               (i_table_id >= 0x50 && i_table_id <= 0x5f) ) ) /* Schedule */
-    {
-        msg_Dbg( p_demux, "PSINewTableCallBack: table 0x%x(%d) ext=0x%x(%d)",
-                 i_table_id, i_table_id, i_extension, i_extension );
-
-        dvbpsi_eit_callback cb = i_table_id == 0x4e ?
-                                    (dvbpsi_eit_callback)EITCallBackCurrentFollowing :
-                                    (dvbpsi_eit_callback)EITCallBackSchedule;
-
-        if( !dvbpsi_eit_attach( h, i_table_id, i_extension, cb, p_demux ) )
-            msg_Err( p_demux, "PSINewTableCallback: failed attaching EITCallback" );
-    }
-    else if( GetPID(p_sys, 0x11)->u.p_psi->i_version != -1 &&
-            (i_table_id == 0x70 /* TDT */ || i_table_id == 0x73 /* TOT */) )
-    {
-         msg_Dbg( p_demux, "PSINewTableCallBack: table 0x%x(%d) ext=0x%x(%d)",
-                 i_table_id, i_table_id, i_extension, i_extension );
-
-        if( !dvbpsi_tot_attach( h, i_table_id, i_extension, (dvbpsi_tot_callback)TDTCallBack, p_demux ) )
-            msg_Err( p_demux, "PSINewTableCallback: failed attaching TDTCallback" );
-    }
-}
-
-/*****************************************************************************
- * PMT callback and helpers
- *****************************************************************************/
-static dvbpsi_descriptor_t *PMTEsFindDescriptor( const dvbpsi_pmt_es_t *p_es,
-                                                 int i_tag )
-{
-    dvbpsi_descriptor_t *p_dr = p_es->p_first_descriptor;;
-    while( p_dr && ( p_dr->i_tag != i_tag ) )
-        p_dr = p_dr->p_next;
-    return p_dr;
-}
-static bool PMTEsHasRegistration( demux_t *p_demux,
-                                  const dvbpsi_pmt_es_t *p_es,
-                                  const char *psz_tag )
-{
-    dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_es, 0x05 );
-    if( !p_dr )
-        return false;
-
-    if( p_dr->i_length < 4 )
-    {
-        msg_Warn( p_demux, "invalid Registration Descriptor" );
-        return false;
-    }
-
-    assert( strlen(psz_tag) == 4 );
-    return !memcmp( p_dr->p_data, psz_tag, 4 );
-}
-
-static bool PMTEsHasComponentTag( const dvbpsi_pmt_es_t *p_es,
-                                  int i_component_tag )
-{
-    dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_es, 0x52 );
-    if( !p_dr )
-        return false;
-    dvbpsi_stream_identifier_dr_t *p_si = dvbpsi_DecodeStreamIdentifierDr( p_dr );
-    if( !p_si )
-        return false;
-
-    return p_si->i_component_tag == i_component_tag;
-}
-
-static const es_mpeg4_descriptor_t * GetMPEG4DescByEsId( const ts_pmt_t *pmt, uint16_t i_es_id )
-{
-    for( int i = 0; i < ES_DESCRIPTOR_COUNT; i++ )
-    {
-        const es_mpeg4_descriptor_t *es_descr = &pmt->iod->es_descr[i];
-        if( es_descr->i_es_id == i_es_id && es_descr->b_ok )
-            return es_descr;
-    }
-    for( int i=0; i<pmt->od.objects.i_size; i++ )
-    {
-        const od_descriptor_t *od = pmt->od.objects.p_elems[i];
-        for( int j = 0; j < ES_DESCRIPTOR_COUNT; j++ )
-        {
-            const es_mpeg4_descriptor_t *es_descr = &od->es_descr[j];
-            if( es_descr->i_es_id == i_es_id && es_descr->b_ok )
-                return es_descr;
-        }
-    }
-    return NULL;
-}
-
-static ts_pes_es_t * GetPMTESBySLEsId( ts_pmt_t *pmt, uint16_t i_sl_es_id )
-{
-    for( int i=0; i< pmt->e_streams.i_size; i++ )
-    {
-        ts_pes_es_t *p_es = pmt->e_streams.p_elems[i]->u.p_pes->p_es;
-        if( p_es->i_sl_es_id == i_sl_es_id )
-            return p_es;
-    }
-    return NULL;
-}
-
-static bool SetupISO14496LogicalStream( demux_t *p_demux, const decoder_config_descriptor_t *dcd,
-                                        es_format_t *p_fmt )
-{
-    msg_Dbg( p_demux, "     - IOD objecttype: %"PRIx8" streamtype:%"PRIx8,
-             dcd->i_objectTypeIndication, dcd->i_streamType );
-
-    if( dcd->i_streamType == 0x04 )    /* VisualStream */
-    {
-        p_fmt->i_cat = VIDEO_ES;
-        switch( dcd->i_objectTypeIndication )
-        {
-        case 0x0B: /* mpeg4 sub */
-            p_fmt->i_cat = SPU_ES;
-            p_fmt->i_codec = VLC_CODEC_SUBT;
-            break;
-
-        case 0x20: /* mpeg4 */
-            p_fmt->i_codec = VLC_CODEC_MP4V;
-            break;
-        case 0x21: /* h264 */
-            p_fmt->i_codec = VLC_CODEC_H264;
-            break;
-        case 0x60:
-        case 0x61:
-        case 0x62:
-        case 0x63:
-        case 0x64:
-        case 0x65: /* mpeg2 */
-            p_fmt->i_codec = VLC_CODEC_MPGV;
-            break;
-        case 0x6a: /* mpeg1 */
-            p_fmt->i_codec = VLC_CODEC_MPGV;
-            break;
-        case 0x6c: /* mpeg1 */
-            p_fmt->i_codec = VLC_CODEC_JPEG;
-            break;
-        default:
-            p_fmt->i_cat = UNKNOWN_ES;
-            break;
-        }
-    }
-    else if( dcd->i_streamType == 0x05 )    /* AudioStream */
-    {
-        p_fmt->i_cat = AUDIO_ES;
-        switch( dcd->i_objectTypeIndication )
-        {
-        case 0x40: /* mpeg4 */
-            p_fmt->i_codec = VLC_CODEC_MP4A;
-            break;
-        case 0x66:
-        case 0x67:
-        case 0x68: /* mpeg2 aac */
-            p_fmt->i_codec = VLC_CODEC_MP4A;
-            break;
-        case 0x69: /* mpeg2 */
-            p_fmt->i_codec = VLC_CODEC_MPGA;
-            break;
-        case 0x6b: /* mpeg1 */
-            p_fmt->i_codec = VLC_CODEC_MPGA;
-            break;
-        default:
-            p_fmt->i_cat = UNKNOWN_ES;
-            break;
-        }
-    }
-    else
-    {
-        p_fmt->i_cat = UNKNOWN_ES;
-    }
-
-    if( p_fmt->i_cat != UNKNOWN_ES )
-    {
-        p_fmt->i_extra = __MIN(dcd->i_extra, INT32_MAX);
-        if( p_fmt->i_extra > 0 )
-        {
-            p_fmt->p_extra = malloc( p_fmt->i_extra );
-            if( p_fmt->p_extra )
-                memcpy( p_fmt->p_extra, dcd->p_extra, p_fmt->i_extra );
-            else
-                p_fmt->i_extra = 0;
-        }
-    }
-
-    return true;
-}
-
-static void SetupAVCDescriptors( demux_t *p_demux, ts_pes_es_t *p_es, const dvbpsi_pmt_es_t *p_dvbpsies )
-{
-    const dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x28 );
-    if( p_dr && p_dr->i_length >= 4 )
-    {
-        p_es->fmt.i_profile = p_dr->p_data[0];
-        p_es->fmt.i_level = p_dr->p_data[2];
-        msg_Dbg( p_demux, "     - found AVC_video_descriptor profile=0x%"PRIx8" level=0x%"PRIx8,
-                 p_es->fmt.i_profile, p_es->fmt.i_level );
-    }
-}
-
-static void SetupJ2KDescriptors( demux_t *p_demux, ts_pes_es_t *p_es, const dvbpsi_pmt_es_t *p_dvbpsies )
-{
-    const dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x32 );
-    if( p_dr && p_dr->i_length >= 24 )
-    {
-        es_format_Init( &p_es->fmt, VIDEO_ES, VLC_CODEC_JPEG2000 );
-        p_es->fmt.i_profile = p_dr->p_data[0];
-        p_es->fmt.i_level = p_dr->p_data[1];
-        p_es->fmt.video.i_width = GetDWBE(&p_dr->p_data[2]);
-        p_es->fmt.video.i_height = GetDWBE(&p_dr->p_data[6]);
-        p_es->fmt.video.i_frame_rate_base = GetWBE(&p_dr->p_data[18]);
-        p_es->fmt.video.i_frame_rate = GetWBE(&p_dr->p_data[20]);
-        p_es->b_interlaced = p_dr->p_data[23] & 0x40;
-        if( p_dr->i_length > 24 )
-        {
-            p_es->fmt.p_extra = malloc(p_dr->i_length - 24);
-            if( p_es->fmt.p_extra )
-                p_es->fmt.i_extra = p_dr->i_length - 24;
-        }
-        msg_Dbg( p_demux, "     - found J2K_video_descriptor profile=0x%"PRIx8" level=0x%"PRIx8,
-                 p_es->fmt.i_profile, p_es->fmt.i_level );
-    }
-}
-
-static void SetupISO14496Descriptors( demux_t *p_demux, ts_pes_t *p_pes,
-                                      const ts_pmt_t *p_pmt, const dvbpsi_pmt_es_t *p_dvbpsies )
-{
-    const dvbpsi_descriptor_t *p_dr = p_dvbpsies->p_first_descriptor;
-    ts_pes_es_t *p_es = p_pes->p_es;
-
-    while( p_dr )
-    {
-        uint8_t i_length = p_dr->i_length;
-
-        switch( p_dr->i_tag )
-        {
-            case 0x1f: /* FMC Descriptor */
-                while( i_length >= 3 && !p_es->i_sl_es_id )
-                {
-                    p_es->i_sl_es_id = ( p_dr->p_data[0] << 8 ) | p_dr->p_data[1];
-                    /* FIXME: map all ids and flexmux channels */
-                    i_length -= 3;
-                    msg_Dbg( p_demux, "     - found FMC_descriptor mapping es_id=%"PRIu16, p_es->i_sl_es_id );
-                }
-                break;
-            case 0x1e: /* SL Descriptor */
-                if( i_length == 2 )
-                {
-                    p_es->i_sl_es_id = ( p_dr->p_data[0] << 8 ) | p_dr->p_data[1];
-                    msg_Dbg( p_demux, "     - found SL_descriptor mapping es_id=%"PRIu16, p_es->i_sl_es_id );
-                    ts_sections_processor_Add( &p_pes->p_sections_proc, 0x05, 0x13,
-                                               false, SLPackets_Section_Handler );
-                    p_pes->b_always_receive = true;
-                }
-                break;
-            default:
-                break;
-        }
-
-        p_dr = p_dr->p_next;
-    }
-
-    if( p_es->i_sl_es_id )
-    {
-        const es_mpeg4_descriptor_t *p_mpeg4desc = GetMPEG4DescByEsId( p_pmt, p_es->i_sl_es_id );
-        if( p_mpeg4desc && p_mpeg4desc->b_ok )
-        {
-            if( !SetupISO14496LogicalStream( p_demux, &p_mpeg4desc->dec_descr, &p_es->fmt ) )
-                msg_Dbg( p_demux, "     - IOD not yet available for es_id=%"PRIu16, p_es->i_sl_es_id );
-        }
-    }
-    else
-    {
-        switch( p_dvbpsies->i_type )
-        {
-        /* non fatal, set by packetizer */
-        case 0x0f: /* ADTS */
-        case 0x11: /* LOAS */
-            msg_Dbg( p_demux, "     - SL/FMC descriptor not found/matched" );
-            break;
-        default:
-            msg_Err( p_demux, "      - SL/FMC descriptor not found/matched" );
-            break;
-        }
-    }
-}
-
-typedef struct
-{
-    int  i_type;
-    int  i_magazine;
-    int  i_page;
-    char p_iso639[3];
-} ts_teletext_page_t;
-
-static void PMTSetupEsTeletext( demux_t *p_demux, ts_pes_t *p_pes,
-                                const dvbpsi_pmt_es_t *p_dvbpsies )
-{
-    es_format_t *p_fmt = &p_pes->p_es->fmt;
-
-    ts_teletext_page_t p_page[2 * 64 + 20];
-    unsigned i_page = 0;
-
-    /* Gather pages information */
-    for( unsigned i_tag_idx = 0; i_tag_idx < 2; i_tag_idx++ )
-    {
-        dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, i_tag_idx == 0 ? 0x46 : 0x56 );
-        if( !p_dr )
-            continue;
-
-        dvbpsi_teletext_dr_t *p_sub = dvbpsi_DecodeTeletextDr( p_dr );
-        if( !p_sub )
-            continue;
-
-        for( int i = 0; i < p_sub->i_pages_number; i++ )
-        {
-            const dvbpsi_teletextpage_t *p_src = &p_sub->p_pages[i];
-
-            if( p_src->i_teletext_type >= 0x06 )
-                continue;
-
-            assert( i_page < sizeof(p_page)/sizeof(*p_page) );
-
-            ts_teletext_page_t *p_dst = &p_page[i_page++];
-
-            p_dst->i_type = p_src->i_teletext_type;
-            p_dst->i_magazine = p_src->i_teletext_magazine_number
-                ? p_src->i_teletext_magazine_number : 8;
-            p_dst->i_page = p_src->i_teletext_page_number;
-            memcpy( p_dst->p_iso639, p_src->i_iso6392_language_code, 3 );
-        }
-    }
-
-    dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x59 );
-    if( p_dr )
-    {
-        dvbpsi_subtitling_dr_t *p_sub = dvbpsi_DecodeSubtitlingDr( p_dr );
-        for( int i = 0; p_sub && i < p_sub->i_subtitles_number; i++ )
-        {
-            dvbpsi_subtitle_t *p_src = &p_sub->p_subtitle[i];
-
-            if( p_src->i_subtitling_type < 0x01 || p_src->i_subtitling_type > 0x03 )
-                continue;
-
-            assert( i_page < sizeof(p_page)/sizeof(*p_page) );
-
-            ts_teletext_page_t *p_dst = &p_page[i_page++];
-
-            switch( p_src->i_subtitling_type )
-            {
-            case 0x01:
-                p_dst->i_type = 0x02;
-                break;
-            default:
-                p_dst->i_type = 0x03;
-                break;
-            }
-            /* FIXME check if it is the right split */
-            p_dst->i_magazine = (p_src->i_composition_page_id >> 8)
-                ? (p_src->i_composition_page_id >> 8) : 8;
-            p_dst->i_page = p_src->i_composition_page_id & 0xff;
-            memcpy( p_dst->p_iso639, p_src->i_iso6392_language_code, 3 );
-        }
-    }
-
-    /* */
-    es_format_Init( p_fmt, SPU_ES, VLC_CODEC_TELETEXT );
-
-    if( !p_demux->p_sys->b_split_es || i_page <= 0 )
-    {
-        p_fmt->subs.teletext.i_magazine = -1;
-        p_fmt->subs.teletext.i_page = 0;
-        p_fmt->psz_description = strdup( vlc_gettext(ppsz_teletext_type[1]) );
-
-        dvbpsi_descriptor_t *p_dr;
-        p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x46 );
-        if( !p_dr )
-            p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x56 );
-
-        if( !p_demux->p_sys->b_split_es && p_dr && p_dr->i_length > 0 )
-        {
-            /* Descriptor pass-through */
-            p_fmt->p_extra = malloc( p_dr->i_length );
-            if( p_fmt->p_extra )
-            {
-                p_fmt->i_extra = p_dr->i_length;
-                memcpy( p_fmt->p_extra, p_dr->p_data, p_dr->i_length );
-            }
-        }
-    }
-    else
-    {
-        for( unsigned i = 0; i < i_page; i++ )
-        {
-            ts_pes_es_t *p_page_es;
-
-            /* */
-            if( i == 0 )
-            {
-                p_page_es = p_pes->p_es;
-            }
-            else
-            {
-                p_page_es = ts_pes_es_New( p_pes->p_es->p_program );
-                if( !p_page_es )
-                    break;
-
-                es_format_Copy( &p_page_es->fmt, p_fmt );
-                free( p_page_es->fmt.psz_language );
-                free( p_page_es->fmt.psz_description );
-                p_page_es->fmt.psz_language = NULL;
-                p_page_es->fmt.psz_description = NULL;
-                ts_pes_Add_es( p_pes, p_page_es, true );
-            }
-
-            /* */
-            const ts_teletext_page_t *p = &p_page[i];
-            p_page_es->fmt.i_priority = (p->i_type == 0x02 || p->i_type == 0x05) ?
-                      ES_PRIORITY_SELECTABLE_MIN : ES_PRIORITY_NOT_DEFAULTABLE;
-            p_page_es->fmt.psz_language = strndup( p->p_iso639, 3 );
-            p_page_es->fmt.psz_description = strdup(vlc_gettext(ppsz_teletext_type[p->i_type]));
-            p_page_es->fmt.subs.teletext.i_magazine = p->i_magazine;
-            p_page_es->fmt.subs.teletext.i_page = p->i_page;
-
-            msg_Dbg( p_demux,
-                         "    * ttxt type=%s lan=%s page=%d%02x",
-                         p_page_es->fmt.psz_description,
-                         p_page_es->fmt.psz_language,
-                         p->i_magazine, p->i_page );
-        }
-    }
-}
-static void PMTSetupEsDvbSubtitle( demux_t *p_demux, ts_pes_t *p_pes,
-                                   const dvbpsi_pmt_es_t *p_dvbpsies )
-{
-    es_format_t *p_fmt = &p_pes->p_es->fmt;
-
-    es_format_Init( p_fmt, SPU_ES, VLC_CODEC_DVBS );
-
-    dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x59 );
-    int i_page = 0;
-    dvbpsi_subtitling_dr_t *p_sub = dvbpsi_DecodeSubtitlingDr( p_dr );
-    for( int i = 0; p_sub && i < p_sub->i_subtitles_number; i++ )
-    {
-        const int i_type = p_sub->p_subtitle[i].i_subtitling_type;
-        if( ( i_type >= 0x10 && i_type <= 0x14 ) ||
-            ( i_type >= 0x20 && i_type <= 0x24 ) )
-            i_page++;
-    }
-
-    if( !p_demux->p_sys->b_split_es  || i_page <= 0 )
-    {
-        p_fmt->subs.dvb.i_id = -1;
-        p_fmt->psz_description = strdup( _("DVB subtitles") );
-
-        if( !p_demux->p_sys->b_split_es && p_dr && p_dr->i_length > 0 )
-        {
-            /* Descriptor pass-through */
-            p_fmt->p_extra = malloc( p_dr->i_length );
-            if( p_fmt->p_extra )
-            {
-                p_fmt->i_extra = p_dr->i_length;
-                memcpy( p_fmt->p_extra, p_dr->p_data, p_dr->i_length );
-            }
-        }
-    }
-    else
-    {
-        for( int i = 0; i < p_sub->i_subtitles_number; i++ )
-        {
-            ts_pes_es_t *p_subs_es;
-
-            /* */
-            if( i == 0 )
-            {
-                p_subs_es = p_pes->p_es;
-            }
-            else
-            {
-                p_subs_es = ts_pes_es_New( p_pes->p_es->p_program );
-                if( !p_subs_es )
-                    break;
-
-                es_format_Copy( &p_subs_es->fmt, p_fmt );
-                free( p_subs_es->fmt.psz_language );
-                free( p_subs_es->fmt.psz_description );
-                p_subs_es->fmt.psz_language = NULL;
-                p_subs_es->fmt.psz_description = NULL;
-
-                ts_pes_Add_es( p_pes, p_subs_es, true );
-            }
-
-            /* */
-            const dvbpsi_subtitle_t *p = &p_sub->p_subtitle[i];
-            p_subs_es->fmt.psz_language = strndup( (char *)p->i_iso6392_language_code, 3 );
-            switch( p->i_subtitling_type )
-            {
-            case 0x10: /* unspec. */
-            case 0x11: /* 4:3 */
-            case 0x12: /* 16:9 */
-            case 0x13: /* 2.21:1 */
-            case 0x14: /* HD monitor */
-                p_subs_es->fmt.psz_description = strdup( _("DVB subtitles") );
-                break;
-            case 0x20: /* Hearing impaired unspec. */
-            case 0x21: /* h.i. 4:3 */
-            case 0x22: /* h.i. 16:9 */
-            case 0x23: /* h.i. 2.21:1 */
-            case 0x24: /* h.i. HD monitor */
-                p_subs_es->fmt.psz_description = strdup( _("DVB subtitles: hearing impaired") );
-                break;
-            default:
-                break;
-            }
-
-            /* Hack, FIXME */
-            p_subs_es->fmt.subs.dvb.i_id = ( p->i_composition_page_id <<  0 ) |
-                                      ( p->i_ancillary_page_id   << 16 );
-        }
-    }
-}
-
-static int vlc_ceil_log2( const unsigned int val )
-{
-    int n = 31 - clz(val);
-    if ((1U << n) != val)
-        n++;
-
-    return n;
-}
-
-static void OpusSetup(demux_t *demux, uint8_t *p, size_t len, es_format_t *p_fmt)
-{
-    OpusHeader h;
-
-    /* default mapping */
-    static const unsigned char map[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
-    memcpy(h.stream_map, map, sizeof(map));
-
-    int csc, mapping;
-    int channels = 0;
-    int stream_count = 0;
-    int ccc = p[1]; // channel_config_code
-    if (ccc <= 8) {
-        channels = ccc;
-        if (channels)
-            mapping = channels > 2;
-        else {
-            mapping = 255;
-            channels = 2; // dual mono
-        }
-        static const uint8_t p_csc[8] = { 0, 1, 1, 2, 2, 2, 3, 3 };
-        csc = p_csc[channels - 1];
-        stream_count = channels - csc;
-
-        static const uint8_t map[6][7] = {
-            { 2,1 },
-            { 1,2,3 },
-            { 4,1,2,3 },
-            { 4,1,2,3,5 },
-            { 4,1,2,3,5,6 },
-            { 6,1,2,3,4,5,7 },
-        };
-        if (channels > 2)
-            memcpy(&h.stream_map[1], map[channels-3], channels - 1);
-    } else if (ccc == 0x81) {
-        if (len < 4)
-            goto explicit_config_too_short;
-
-        channels = p[2];
-        mapping = p[3];
-        csc = 0;
-        if (mapping) {
-            bs_t s;
-            bs_init(&s, &p[4], len - 4);
-            stream_count = 1;
-            if (channels) {
-                int bits = vlc_ceil_log2(channels);
-                if (s.i_left < bits)
-                    goto explicit_config_too_short;
-                stream_count = bs_read(&s, bits) + 1;
-                bits = vlc_ceil_log2(stream_count + 1);
-                if (s.i_left < bits)
-                    goto explicit_config_too_short;
-                csc = bs_read(&s, bits);
-            }
-            int channel_bits = vlc_ceil_log2(stream_count + csc + 1);
-            if (s.i_left < channels * channel_bits)
-                goto explicit_config_too_short;
-
-            unsigned char silence = (1U << (stream_count + csc + 1)) - 1;
-            for (int i = 0; i < channels; i++) {
-                unsigned char m = bs_read(&s, channel_bits);
-                if (m == silence)
-                    m = 0xff;
-                h.stream_map[i] = m;
-            }
-        }
-    } else if (ccc >= 0x80 && ccc <= 0x88) {
-        channels = ccc - 0x80;
-        if (channels)
-            mapping = 1;
-        else {
-            mapping = 255;
-            channels = 2; // dual mono
-        }
-        csc = 0;
-        stream_count = channels;
-    } else {
-        msg_Err(demux, "Opus channel configuration 0x%.2x is reserved", ccc);
-    }
-
-    if (!channels) {
-        msg_Err(demux, "Opus channel configuration 0x%.2x not supported yet", p[1]);
-        return;
-    }
-
-    opus_prepare_header(channels, 0, &h);
-    h.preskip = 0;
-    h.input_sample_rate = 48000;
-    h.nb_coupled = csc;
-    h.nb_streams = channels - csc;
-    h.channel_mapping = mapping;
-
-    if (h.channels) {
-        opus_write_header((uint8_t**)&p_fmt->p_extra, &p_fmt->i_extra, &h, NULL /* FIXME */);
-        if (p_fmt->p_extra) {
-            p_fmt->i_cat = AUDIO_ES;
-            p_fmt->i_codec = VLC_CODEC_OPUS;
-            p_fmt->audio.i_channels = h.channels;
-            p_fmt->audio.i_rate = 48000;
-        }
-    }
-
-    return;
-
-explicit_config_too_short:
-    msg_Err(demux, "Opus descriptor too short");
-}
-
-static void PMTSetupEs0x06( demux_t *p_demux, ts_pes_t *p_pes,
-                            const dvbpsi_pmt_es_t *p_dvbpsies )
-{
-    es_format_t *p_fmt = &p_pes->p_es->fmt;
-    dvbpsi_descriptor_t *p_subs_dr = PMTEsFindDescriptor( p_dvbpsies, 0x59 );
-    dvbpsi_descriptor_t *desc;
-    if( PMTEsHasRegistration( p_demux, p_dvbpsies, "EAC3" ) ||
-        PMTEsFindDescriptor( p_dvbpsies, 0x7a ) )
-    {
-        /* DVB with stream_type 0x06 (ETS EN 300 468) */
-        p_fmt->i_cat = AUDIO_ES;
-        p_fmt->i_codec = VLC_CODEC_EAC3;
-    }
-    else if( PMTEsHasRegistration( p_demux, p_dvbpsies, "AC-3" ) ||
-             PMTEsFindDescriptor( p_dvbpsies, 0x6a ) ||
-             PMTEsFindDescriptor( p_dvbpsies, 0x81 ) ) /* AC-3 channel (also in EAC3) */
-    {
-        p_fmt->i_cat = AUDIO_ES;
-        p_fmt->i_codec = VLC_CODEC_A52;
-    }
-    else if( (desc = PMTEsFindDescriptor( p_dvbpsies, 0x7f ) ) && desc->i_length >= 2 &&
-              PMTEsHasRegistration(p_demux, p_dvbpsies, "Opus"))
-    {
-        OpusSetup(p_demux, desc->p_data, desc->i_length, p_fmt);
-    }
-    else if( PMTEsHasRegistration( p_demux, p_dvbpsies, "DTS1" ) ||
-             PMTEsHasRegistration( p_demux, p_dvbpsies, "DTS2" ) ||
-             PMTEsHasRegistration( p_demux, p_dvbpsies, "DTS3" ) ||
-             PMTEsFindDescriptor( p_dvbpsies, 0x73 ) )
-    {
-        /*registration descriptor(ETSI TS 101 154 Annex F)*/
-        p_fmt->i_cat = AUDIO_ES;
-        p_fmt->i_codec = VLC_CODEC_DTS;
-    }
-    else if( PMTEsHasRegistration( p_demux, p_dvbpsies, "BSSD" ) && !p_subs_dr )
-    {
-        /* BSSD is AES3 DATA, but could also be subtitles
-         * we need to check for secondary descriptor then s*/
-        p_fmt->i_cat = AUDIO_ES;
-        p_fmt->b_packetized = true;
-        p_fmt->i_codec = VLC_CODEC_302M;
-    }
-    else if( PMTEsHasRegistration( p_demux, p_dvbpsies, "HEVC" ) )
-    {
-        p_fmt->i_cat = VIDEO_ES;
-        p_fmt->i_codec = VLC_CODEC_HEVC;
-    }
-    else if ( p_demux->p_sys->arib.e_mode == ARIBMODE_ENABLED )
-    {
-        /* Lookup our data component descriptor first ARIB STD B10 6.4 */
-        dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0xFD );
-        /* and check that it maps to something ARIB STD B14 Table 5.1/5.2 */
-        if ( p_dr && p_dr->i_length >= 2 )
-        {
-            if( !memcmp( p_dr->p_data, "\x00\x08", 2 ) &&  (
-                    PMTEsHasComponentTag( p_dvbpsies, 0x30 ) ||
-                    PMTEsHasComponentTag( p_dvbpsies, 0x31 ) ||
-                    PMTEsHasComponentTag( p_dvbpsies, 0x32 ) ||
-                    PMTEsHasComponentTag( p_dvbpsies, 0x33 ) ||
-                    PMTEsHasComponentTag( p_dvbpsies, 0x34 ) ||
-                    PMTEsHasComponentTag( p_dvbpsies, 0x35 ) ||
-                    PMTEsHasComponentTag( p_dvbpsies, 0x36 ) ||
-                    PMTEsHasComponentTag( p_dvbpsies, 0x37 ) ) )
-            {
-                es_format_Init( p_fmt, SPU_ES, VLC_CODEC_ARIB_A );
-                p_fmt->psz_language = strndup ( "jpn", 3 );
-                p_fmt->psz_description = strdup( _("ARIB subtitles") );
-            }
-            else if( !memcmp( p_dr->p_data, "\x00\x12", 2 ) && (
-                     PMTEsHasComponentTag( p_dvbpsies, 0x87 ) ||
-                     PMTEsHasComponentTag( p_dvbpsies, 0x88 ) ) )
-            {
-                es_format_Init( p_fmt, SPU_ES, VLC_CODEC_ARIB_C );
-                p_fmt->psz_language = strndup ( "jpn", 3 );
-                p_fmt->psz_description = strdup( _("ARIB subtitles") );
-            }
-        }
-    }
-    else
-    {
-        /* Subtitle/Teletext/VBI fallbacks */
-        dvbpsi_subtitling_dr_t *p_sub;
-        if( p_subs_dr && ( p_sub = dvbpsi_DecodeSubtitlingDr( p_subs_dr ) ) )
-        {
-            for( int i = 0; i < p_sub->i_subtitles_number; i++ )
-            {
-                if( p_fmt->i_cat != UNKNOWN_ES )
-                    break;
-
-                switch( p_sub->p_subtitle[i].i_subtitling_type )
-                {
-                case 0x01: /* EBU Teletext subtitles */
-                case 0x02: /* Associated EBU Teletext */
-                case 0x03: /* VBI data */
-                    PMTSetupEsTeletext( p_demux, p_pes, p_dvbpsies );
-                    break;
-                case 0x10: /* DVB Subtitle (normal) with no monitor AR critical */
-                case 0x11: /*                 ...   on 4:3 AR monitor */
-                case 0x12: /*                 ...   on 16:9 AR monitor */
-                case 0x13: /*                 ...   on 2.21:1 AR monitor */
-                case 0x14: /*                 ...   for display on a high definition monitor */
-                case 0x20: /* DVB Subtitle (impaired) with no monitor AR critical */
-                case 0x21: /*                 ...   on 4:3 AR monitor */
-                case 0x22: /*                 ...   on 16:9 AR monitor */
-                case 0x23: /*                 ...   on 2.21:1 AR monitor */
-                case 0x24: /*                 ...   for display on a high definition monitor */
-                    PMTSetupEsDvbSubtitle( p_demux, p_pes, p_dvbpsies );
-                    break;
-                default:
-                    msg_Err( p_demux, "Unrecognized DVB subtitle type (0x%x)",
-                             p_sub->p_subtitle[i].i_subtitling_type );
-                    break;
-                }
-            }
-        }
-
-        if( p_fmt->i_cat == UNKNOWN_ES &&
-            ( PMTEsFindDescriptor( p_dvbpsies, 0x45 ) ||  /* VBI Data descriptor */
-              PMTEsFindDescriptor( p_dvbpsies, 0x46 ) ||  /* VBI Teletext descriptor */
-              PMTEsFindDescriptor( p_dvbpsies, 0x56 ) ) ) /* EBU Teletext descriptor */
-        {
-            /* Teletext/VBI */
-            PMTSetupEsTeletext( p_demux, p_pes, p_dvbpsies );
-        }
-    }
-
-    /* FIXME is it useful ? */
-    if( PMTEsFindDescriptor( p_dvbpsies, 0x52 ) )
-    {
-        dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x52 );
-        dvbpsi_stream_identifier_dr_t *p_si = dvbpsi_DecodeStreamIdentifierDr( p_dr );
-
-        msg_Dbg( p_demux, "    * Stream Component Identifier: %d", p_si->i_component_tag );
-    }
-}
-
-static void PMTSetupEs0xEA( demux_t *p_demux, ts_pes_es_t *p_es,
-                           const dvbpsi_pmt_es_t *p_dvbpsies )
-{
-    /* Registration Descriptor */
-    if( !PMTEsHasRegistration( p_demux, p_dvbpsies, "VC-1" ) )
-    {
-        msg_Err( p_demux, "Registration descriptor not found or invalid" );
-        return;
-    }
-
-    es_format_t *p_fmt = &p_es->fmt;
-
-    /* registration descriptor for VC-1 (SMPTE rp227) */
-    p_fmt->i_cat = VIDEO_ES;
-    p_fmt->i_codec = VLC_CODEC_VC1;
-
-    /* XXX With Simple and Main profile the SEQUENCE
-     * header is modified: video width and height are
-     * inserted just after the start code as 2 int16_t
-     * The packetizer will take care of that. */
-}
-
-static void PMTSetupEs0xD1( demux_t *p_demux, ts_pes_es_t *p_es,
-                           const dvbpsi_pmt_es_t *p_dvbpsies )
-{
-    /* Registration Descriptor */
-    if( !PMTEsHasRegistration( p_demux, p_dvbpsies, "drac" ) )
-    {
-        msg_Err( p_demux, "Registration descriptor not found or invalid" );
-        return;
-    }
-
-    es_format_t *p_fmt = &p_es->fmt;
-
-    /* registration descriptor for Dirac
-     * (backwards compatable with VC-2 (SMPTE Sxxxx:2008)) */
-    p_fmt->i_cat = VIDEO_ES;
-    p_fmt->i_codec = VLC_CODEC_DIRAC;
-}
-
-static void PMTSetupEs0xA0( demux_t *p_demux, ts_pes_es_t *p_es,
-                           const dvbpsi_pmt_es_t *p_dvbpsies )
-{
-    /* MSCODEC sent by vlc */
-    dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0xa0 );
-    if( !p_dr || p_dr->i_length < 10 )
-    {
-        msg_Warn( p_demux,
-                  "private MSCODEC (vlc) without bih private descriptor" );
-        return;
-    }
-
-    es_format_t *p_fmt = &p_es->fmt;
-    p_fmt->i_cat = VIDEO_ES;
-    p_fmt->i_codec = VLC_FOURCC( p_dr->p_data[0], p_dr->p_data[1],
-                                 p_dr->p_data[2], p_dr->p_data[3] );
-    p_fmt->video.i_width = GetWBE( &p_dr->p_data[4] );
-    p_fmt->video.i_height = GetWBE( &p_dr->p_data[6] );
-    p_fmt->video.i_visible_width = p_fmt->video.i_width;
-    p_fmt->video.i_visible_height = p_fmt->video.i_height;
-    p_fmt->i_extra = GetWBE( &p_dr->p_data[8] );
-
-    if( p_fmt->i_extra > 0 )
-    {
-        p_fmt->p_extra = malloc( p_fmt->i_extra );
-        if( p_fmt->p_extra )
-            memcpy( p_fmt->p_extra, &p_dr->p_data[10],
-                    __MIN( p_fmt->i_extra, p_dr->i_length - 10 ) );
-        else
-            p_fmt->i_extra = 0;
-    }
-    /* For such stream we will gather them ourself and don't launch a
-     * packetizer.
-     * Yes it's ugly but it's the only way to have DIV3 working */
-    p_fmt->b_packetized = true;
-}
-
-static void PMTSetupEs0x83( const dvbpsi_pmt_t *p_pmt, ts_pes_es_t *p_es, int i_pid )
-{
-    /* WiDi broadcasts without registration on PMT 0x1, PCR 0x1000 and
-     * with audio track pid being 0x1100..0x11FF */
-    if ( p_pmt->i_program_number == 0x1 &&
-         p_pmt->i_pcr_pid == 0x1000 &&
-        ( i_pid >> 8 ) == 0x11 )
-    {
-        /* Not enough ? might contain 0x83 private descriptor, 2 bytes 0x473F */
-        es_format_Init( &p_es->fmt, AUDIO_ES, VLC_CODEC_WIDI_LPCM );
-    }
-    else
-        es_format_Init( &p_es->fmt, AUDIO_ES, VLC_CODEC_DVD_LPCM );
-}
-
-static bool PMTSetupEsHDMV( demux_t *p_demux, ts_pes_es_t *p_es,
-                            const dvbpsi_pmt_es_t *p_dvbpsies )
-{
-    es_format_t *p_fmt = &p_es->fmt;
-
-    /* Blu-Ray mapping */
-    switch( p_dvbpsies->i_type )
-    {
-    case 0x80:
-        p_fmt->i_cat = AUDIO_ES;
-        p_fmt->i_codec = VLC_CODEC_BD_LPCM;
-        break;
-    case 0x81:
-        p_fmt->i_cat = AUDIO_ES;
-        p_fmt->i_codec = VLC_CODEC_A52;
-        break;
-    case 0x82:
-    case 0x85: /* DTS-HD High resolution audio */
-    case 0x86: /* DTS-HD Master audio */
-    case 0xA2: /* Secondary DTS audio */
-        p_fmt->i_cat = AUDIO_ES;
-        p_fmt->i_codec = VLC_CODEC_DTS;
-        break;
-
-    case 0x83: /* TrueHD AC3 */
-        p_fmt->i_cat = AUDIO_ES;
-        p_fmt->i_codec = VLC_CODEC_TRUEHD;
-        break;
-
-    case 0x84: /* E-AC3 */
-    case 0xA1: /* Secondary E-AC3 */
-        p_fmt->i_cat = AUDIO_ES;
-        p_fmt->i_codec = VLC_CODEC_EAC3;
-        break;
-    case 0x90: /* Presentation graphics */
-        p_fmt->i_cat = SPU_ES;
-        p_fmt->i_codec = VLC_CODEC_BD_PG;
-        break;
-    case 0x91: /* Interactive graphics */
-    case 0x92: /* Subtitle */
-        return false;
-    case 0xEA:
-        p_fmt->i_cat = VIDEO_ES;
-        p_fmt->i_codec = VLC_CODEC_VC1;
-        break;
-    default:
-        msg_Info( p_demux, "HDMV registration not implemented for pid 0x%x type 0x%x",
-                  p_dvbpsies->i_pid, p_dvbpsies->i_type );
-        return false;
-        break;
-    }
-    return true;
-}
-
-static bool PMTSetupEsRegistration( demux_t *p_demux, ts_pes_es_t *p_es,
-                                    const dvbpsi_pmt_es_t *p_dvbpsies )
-{
-    static const struct
-    {
-        char         psz_tag[5];
-        int          i_cat;
-        vlc_fourcc_t i_codec;
-    } p_regs[] = {
-        { "AC-3", AUDIO_ES, VLC_CODEC_A52   },
-        { "DTS1", AUDIO_ES, VLC_CODEC_DTS   },
-        { "DTS2", AUDIO_ES, VLC_CODEC_DTS   },
-        { "DTS3", AUDIO_ES, VLC_CODEC_DTS   },
-        { "BSSD", AUDIO_ES, VLC_CODEC_302M  },
-        { "VC-1", VIDEO_ES, VLC_CODEC_VC1   },
-        { "drac", VIDEO_ES, VLC_CODEC_DIRAC },
-        { "", UNKNOWN_ES, 0 }
-    };
-    es_format_t *p_fmt = &p_es->fmt;
-
-    for( int i = 0; p_regs[i].i_cat != UNKNOWN_ES; i++ )
-    {
-        if( PMTEsHasRegistration( p_demux, p_dvbpsies, p_regs[i].psz_tag ) )
-        {
-            p_fmt->i_cat   = p_regs[i].i_cat;
-            p_fmt->i_codec = p_regs[i].i_codec;
-            if (p_dvbpsies->i_type == 0x87)
-                p_fmt->i_codec = VLC_CODEC_EAC3;
-            return true;
-        }
-    }
-    return false;
-}
-
-static char *GetAudioTypeDesc(demux_t *p_demux, int type)
-{
-    static const char *audio_type[] = {
-        NULL,
-        N_("clean effects"),
-        N_("hearing impaired"),
-        N_("visual impaired commentary"),
-    };
-
-    if (type < 0 || type > 3)
-        msg_Dbg( p_demux, "unknown audio type: %d", type);
-    else if (type > 0)
-        return strdup(audio_type[type]);
-
-    return NULL;
-}
-static void PMTParseEsIso639( demux_t *p_demux, ts_pes_es_t *p_es,
-                              const dvbpsi_pmt_es_t *p_dvbpsies )
-{
-    /* get language descriptor */
-    dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x0a );
-
-    if( !p_dr )
-        return;
-
-    dvbpsi_iso639_dr_t *p_decoded = dvbpsi_DecodeISO639Dr( p_dr );
-    if( !p_decoded )
-    {
-        msg_Err( p_demux, "      Failed to decode a ISO 639 descriptor" );
-        return;
-    }
-
-#if defined(DR_0A_API_VER) && (DR_0A_API_VER >= 2)
-    p_es->fmt.psz_language = malloc( 4 );
-    if( p_es->fmt.psz_language )
-    {
-        memcpy( p_es->fmt.psz_language, p_decoded->code[0].iso_639_code, 3 );
-        p_es->fmt.psz_language[3] = 0;
-        msg_Dbg( p_demux, "      found language: %s", p_es->fmt.psz_language);
-    }
-    int type = p_decoded->code[0].i_audio_type;
-    p_es->fmt.psz_description = GetAudioTypeDesc(p_demux, type);
-    if (type == 0)
-        p_es->fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN + 1; // prioritize normal audio tracks
-
-    p_es->fmt.i_extra_languages = p_decoded->i_code_count-1;
-    if( p_es->fmt.i_extra_languages > 0 )
-        p_es->fmt.p_extra_languages =
-            malloc( sizeof(*p_es->fmt.p_extra_languages) *
-                    p_es->fmt.i_extra_languages );
-    if( p_es->fmt.p_extra_languages )
-    {
-        for( unsigned i = 0; i < p_es->fmt.i_extra_languages; i++ )
-        {
-            p_es->fmt.p_extra_languages[i].psz_language = malloc(4);
-            if( p_es->fmt.p_extra_languages[i].psz_language )
-            {
-                memcpy( p_es->fmt.p_extra_languages[i].psz_language,
-                    p_decoded->code[i+1].iso_639_code, 3 );
-                p_es->fmt.p_extra_languages[i].psz_language[3] = '\0';
-            }
-            int type = p_decoded->code[i].i_audio_type;
-            p_es->fmt.p_extra_languages[i].psz_description = GetAudioTypeDesc(p_demux, type);
-        }
-    }
-#else
-    p_es->fmt.psz_language = malloc( 4 );
-    if( p_es->fmt.psz_language )
-    {
-        memcpy( p_es->fmt.psz_language,
-                p_decoded->i_iso_639_code, 3 );
-        p_es->fmt.psz_language[3] = 0;
-    }
-#endif
-}
-
-static void DoCreateES( demux_t *p_demux, ts_pes_es_t *p_es, const ts_pes_es_t *p_parent_es )
-{
-    demux_sys_t *p_sys = p_demux->p_sys;
-    for( ; p_es ; p_es = p_es->p_next )
-    {
-        if( !p_es->id )
-        {
-            if( !p_es->fmt.i_group )
-                p_es->fmt.i_group = p_es->p_program->i_number;
-            p_es->id = es_out_Add( p_demux->out, &p_es->fmt );
-            if( p_parent_es ) /* Set Extra ES group and original ID */
-            {
-                if ( p_sys->b_es_id_pid ) /* pid is 13 bits */
-                    p_es->fmt.i_id = (p_sys->i_next_extraid++ << 13) | p_parent_es->fmt.i_id;
-                p_es->fmt.i_group = p_parent_es->fmt.i_group;
-            }
-            p_sys->i_pmt_es++;
-        }
-        DoCreateES( p_demux, p_es->p_extraes, p_es );
-    }
-}
-
-static void AddAndCreateES( demux_t *p_demux, ts_pid_t *pid, bool b_create_delayed )
-{
-    demux_sys_t  *p_sys = p_demux->p_sys;
-
-    if( b_create_delayed )
-        p_sys->es_creation = CREATE_ES;
-
-    if( pid && p_sys->es_creation == CREATE_ES )
-    {
-        DoCreateES( p_demux, pid->u.p_pes->p_es, NULL );
-
-        /* Update the default program == first created ES group */
-        if( p_sys->b_default_selection && p_sys->programs.i_size > 0)
-        {
-            p_sys->b_default_selection = false;
-            const int i_first_program = pid->u.p_pes->p_es->p_program->i_number;
-            if( p_sys->programs.p_elems[0] != i_first_program )
-                p_sys->programs.p_elems[0] = i_first_program;
-            msg_Dbg( p_demux, "Default program is %d", i_first_program );
-        }
-    }
-
-    if( b_create_delayed )
-    {
-        ts_pat_t *p_pat = GetPID(p_sys, 0)->u.p_pat;
-        for( int i=0; i< p_pat->programs.i_size; i++ )
-        {
-            ts_pmt_t *p_pmt = p_pat->programs.p_elems[i]->u.p_pmt;
-            for( int j=0; j<p_pmt->e_streams.i_size; j++ )
-                DoCreateES( p_demux, p_pmt->e_streams.p_elems[j]->u.p_pes->p_es, NULL );
-        }
-    }
-
-    UpdatePESFilters( p_demux, p_sys->b_es_all );
-}
-
-static void FillPESFromDvbpsiES( demux_t *p_demux,
-                                 const dvbpsi_pmt_t *p_dvbpsipmt,
-                                 const dvbpsi_pmt_es_t *p_dvbpsies,
-                                 ts_pmt_registration_type_t registration_type,
-                                 const ts_pmt_t *p_pmt,
-                                 ts_pes_t *p_pes )
-{
-    ts_es_data_type_t type_change = TS_ES_DATA_PES;
-    PIDFillFormat( p_pes, p_dvbpsies->i_type, &type_change );
-
-    p_pes->i_stream_type = p_dvbpsies->i_type;
-
-    bool b_registration_applied = false;
-    if ( p_dvbpsies->i_type >= 0x80 ) /* non standard, extensions */
-    {
-        if ( registration_type == TS_PMT_REGISTRATION_BLURAY )
-        {
-            if (( b_registration_applied = PMTSetupEsHDMV( p_demux, p_pes->p_es, p_dvbpsies ) ))
-                msg_Dbg( p_demux, "    + HDMV registration applied to pid %d type 0x%x",
-                         p_dvbpsies->i_pid, p_dvbpsies->i_type );
-        }
-        else
-        {
-            if (( b_registration_applied = PMTSetupEsRegistration( p_demux, p_pes->p_es, p_dvbpsies ) ))
-                msg_Dbg( p_demux, "    + registration applied to pid %d type 0x%x",
-                    p_dvbpsies->i_pid, p_dvbpsies->i_type );
-        }
-    }
-
-    if ( !b_registration_applied )
-    {
-        p_pes->data_type = type_change; /* Only change type if registration has not changed meaning */
-
-        switch( p_dvbpsies->i_type )
-        {
-        case 0x06:
-            /* Handle PES private data */
-            PMTSetupEs0x06( p_demux, p_pes, p_dvbpsies );
-            break;
-        /* All other private or reserved types */
-        case 0x13: /* SL in sections */
-            p_pes->data_type = TS_ES_DATA_TABLE_SECTION;
-            //ft
-        case 0x0f:
-        case 0x10:
-        case 0x11:
-        case 0x12:
-            SetupISO14496Descriptors( p_demux, p_pes, p_pmt, p_dvbpsies );
-            break;
-        case 0x1b:
-            SetupAVCDescriptors( p_demux, p_pes->p_es, p_dvbpsies );
-            break;
-        case 0x21:
-            SetupJ2KDescriptors( p_demux, p_pes->p_es, p_dvbpsies );
-            break;
-        case 0x83:
-            /* LPCM (audio) */
-            PMTSetupEs0x83( p_dvbpsipmt, p_pes->p_es, p_dvbpsies->i_pid );
-            break;
-        case 0xa0:
-            PMTSetupEs0xA0( p_demux, p_pes->p_es, p_dvbpsies );
-            break;
-        case 0xd1:
-            PMTSetupEs0xD1( p_demux, p_pes->p_es, p_dvbpsies );
-            break;
-        case 0xEA:
-            PMTSetupEs0xEA( p_demux, p_pes->p_es, p_dvbpsies );
-        default:
-            break;
-        }
-    }
-
-    if( p_pes->p_es->fmt.i_cat == AUDIO_ES ||
-        ( p_pes->p_es->fmt.i_cat == SPU_ES &&
-          p_pes->p_es->fmt.i_codec != VLC_CODEC_DVBS &&
-          p_pes->p_es->fmt.i_codec != VLC_CODEC_TELETEXT ) )
-    {
-        PMTParseEsIso639( p_demux, p_pes->p_es, p_dvbpsies );
-    }
-
-    /* Set Groups / ID */
-    p_pes->p_es->fmt.i_group = p_dvbpsipmt->i_program_number;
-    if( p_demux->p_sys->b_es_id_pid )
-        p_pes->p_es->fmt.i_id = p_dvbpsies->i_pid;
-}
-
-#define PMT_DESC_PREFIX " * PMT descriptor: "
-#define PMT_DESC_INDENT "                 : "
-static void ParsePMTRegistrations( demux_t *p_demux, const dvbpsi_descriptor_t  *p_firstdr,
-                                   ts_pmt_t *p_pmt, ts_pmt_registration_type_t *p_registration_type )
-{
-    demux_sys_t *p_sys = p_demux->p_sys;
-    ts_pmt_registration_type_t registration_type = *p_registration_type;
-    int i_arib_score_flags = 0; /* Descriptors can be repeated */
-
-    for( const dvbpsi_descriptor_t *p_dr = p_firstdr; p_dr != NULL; p_dr = p_dr->p_next )
-    {
-        /* general descriptors handling < 0x40 and scoring */
-        if( p_dr->i_tag < 0x40 )
-        {
-            msg_Dbg( p_demux, PMT_DESC_PREFIX "%s (0x%x)",
-                     ISO13818_1_Get_Descriptor_Description(p_dr->i_tag), p_dr->i_tag );
-        }
-
-        switch(p_dr->i_tag)
-        {
-            case 0x05: /* Registration Descriptor */
-            {
-                if( p_dr->i_length != 4 )
-                {
-                    msg_Warn( p_demux, PMT_DESC_INDENT "invalid registration descriptor" );
-                    break;
-                }
-
-                static const struct
-                {
-                    const char rgs[4];
-                    const ts_pmt_registration_type_t reg;
-                } regs[] = {
-                    { { 'H', 'D', 'M', 'V' }, TS_PMT_REGISTRATION_BLURAY },
-                    { { 'H', 'D', 'P', 'R' }, TS_PMT_REGISTRATION_BLURAY },
-                    { { 'G', 'A', '9', '4' }, TS_PMT_REGISTRATION_ATSC },
-                };
-
-                for( unsigned i=0; i<ARRAY_SIZE(regs); i++ )
-                {
-                    if( !memcmp( regs[i].rgs, p_dr->p_data, 4 ) )
-                    {
-                        registration_type = regs[i].reg;
-                        msg_Dbg( p_demux, PMT_DESC_INDENT "%4.4s registration", p_dr->p_data );
-                        break;
-                    }
-                }
-            }
-            break;
-
-            case 0x09:
-            {
-                dvbpsi_ca_dr_t *p_cadr = dvbpsi_DecodeCADr( (dvbpsi_descriptor_t *) p_dr );
-                msg_Dbg( p_demux, PMT_DESC_INDENT "CA System ID 0x%x", p_cadr->i_ca_system_id );
-                i_arib_score_flags |= (p_cadr->i_ca_system_id == 0x05);
-            }
-            break;
-
-            case 0x1d: /* We have found an IOD descriptor */
-                p_pmt->iod = IODNew( VLC_OBJECT(p_demux), p_dr->i_length, p_dr->p_data );
-                break;
-
-            case 0xC1:
-                i_arib_score_flags |= 1 << 2;
-                break;
-
-            case 0xF6:
-                i_arib_score_flags |= 1 << 1;
-                break;
-
-            default:
-                break;
-        }
-    }
-
-    if ( p_sys->arib.e_mode == ARIBMODE_AUTO &&
-         registration_type == TS_PMT_REGISTRATION_NONE &&
-         i_arib_score_flags == 0x07 ) //0b111
-    {
-        registration_type = TS_PMT_REGISTRATION_ARIB;
-        p_sys->arib.e_mode = ARIBMODE_ENABLED;
-    }
-
-    /* Now process private descriptors >= 0x40 */
-    for( const dvbpsi_descriptor_t *p_dr = p_firstdr; p_dr != NULL; p_dr = p_dr->p_next )
-    {
-        if( p_dr->i_tag < 0x40 )
-            continue;
-
-        switch(p_dr->i_tag)
-        {
-            case 0x88: /* EACEM Simulcast HD Logical channels ordering */
-                if( registration_type == TS_PMT_REGISTRATION_NONE )
-                    msg_Dbg( p_demux, PMT_DESC_PREFIX "EACEM Simulcast HD" );
-                    /* TODO: apply visibility flags */
-                break;
-
-            case 0xC1:
-                if( registration_type == TS_PMT_REGISTRATION_ARIB )
-                    msg_Dbg( p_demux, PMT_DESC_PREFIX "Digital copy control (0xC1)" );
-                break;
-
-            case 0xDE:
-                if( registration_type == TS_PMT_REGISTRATION_ARIB )
-                    msg_Dbg( p_demux, PMT_DESC_PREFIX "Content availability (0xDE)" );
-                break;
-
-            default:
-                msg_Dbg( p_demux, PMT_DESC_PREFIX "Unknown Private (0x%x)", p_dr->i_tag );
-                break;
-        }
-    }
-
-    *p_registration_type = registration_type;
-}
-
-static void PMTCallBack( void *data, dvbpsi_pmt_t *p_dvbpsipmt )
-{
-    demux_t      *p_demux = data;
-    demux_sys_t  *p_sys = p_demux->p_sys;
-
-    ts_pid_t     *pmtpid = NULL;
-    ts_pmt_t     *p_pmt = NULL;
-
-    msg_Dbg( p_demux, "PMTCallBack called for program %d", p_dvbpsipmt->i_program_number );
-
-    if (unlikely(GetPID(p_sys, 0)->type != TYPE_PAT))
-    {
-        assert(GetPID(p_sys, 0)->type == TYPE_PAT);
-        dvbpsi_pmt_delete(p_dvbpsipmt);
-    }
-
-    const ts_pat_t *p_pat = GetPID(p_sys, 0)->u.p_pat;
-
-    /* First find this PMT declared in PAT */
-    for( int i = 0; !pmtpid && i < p_pat->programs.i_size; i++ )
-    {
-        const int i_pmt_prgnumber = p_pat->programs.p_elems[i]->u.p_pmt->i_number;
-        if( i_pmt_prgnumber != TS_USER_PMT_NUMBER &&
-            i_pmt_prgnumber == p_dvbpsipmt->i_program_number )
-        {
-            pmtpid = p_pat->programs.p_elems[i];
-            assert(pmtpid->type == TYPE_PMT);
-            p_pmt = pmtpid->u.p_pmt;
-        }
-    }
-
-    if( pmtpid == NULL )
-    {
-        msg_Warn( p_demux, "unreferenced program (broken stream)" );
-        dvbpsi_pmt_delete(p_dvbpsipmt);
-        return;
-    }
-
-    pmtpid->i_flags |= FLAG_SEEN;
-
-    if( p_pmt->i_version != -1 &&
-        ( !p_dvbpsipmt->b_current_next || p_pmt->i_version == p_dvbpsipmt->i_version ) )
-    {
-        dvbpsi_pmt_delete( p_dvbpsipmt );
-        return;
-    }
-
-    /* Save old es array */
-    DECL_ARRAY(ts_pid_t *) old_es_rm;
-    old_es_rm.i_alloc = p_pmt->e_streams.i_alloc;
-    old_es_rm.i_size = p_pmt->e_streams.i_size;
-    old_es_rm.p_elems = p_pmt->e_streams.p_elems;
-    ARRAY_INIT(p_pmt->e_streams);
-
-    if( p_pmt->iod )
-    {
-        ODFree( p_pmt->iod );
-        p_pmt->iod = NULL;
-    }
-
-    msg_Dbg( p_demux, "new PMT program number=%d version=%d pid_pcr=%d",
-             p_dvbpsipmt->i_program_number, p_dvbpsipmt->i_version, p_dvbpsipmt->i_pcr_pid );
-    p_pmt->i_pid_pcr = p_dvbpsipmt->i_pcr_pid;
-    p_pmt->i_version = p_dvbpsipmt->i_version;
-
-    ValidateDVBMeta( p_demux, p_pmt->i_pid_pcr );
-
-    if( ProgramIsSelected( p_sys, p_pmt->i_number ) )
-        SetPIDFilter( p_sys, GetPID(p_sys, p_pmt->i_pid_pcr), true ); /* Set demux filter */
-
-    /* Parse PMT descriptors */
-    ts_pmt_registration_type_t registration_type = TS_PMT_REGISTRATION_NONE;
-    ParsePMTRegistrations( p_demux, p_dvbpsipmt->p_first_descriptor, p_pmt, &registration_type );
-
-    dvbpsi_pmt_es_t *p_dvbpsies;
-    for( p_dvbpsies = p_dvbpsipmt->p_first_es; p_dvbpsies != NULL; p_dvbpsies = p_dvbpsies->p_next )
-    {
-        /* Do not mix with arbitrary pid if any */
-        if( p_sys->b_atsc_eas && p_dvbpsies->i_pid == SCTE18_SI_BASE_PID )
-            continue;
-
-        ts_pid_t *pespid = GetPID(p_sys, p_dvbpsies->i_pid);
-        if ( pespid->type != TYPE_PES && pespid->type != TYPE_FREE )
-        {
-            msg_Warn( p_demux, " * PMT wants to create PES on pid %d used by non PES", pespid->i_pid );
-            continue;
-        }
-
-        ValidateDVBMeta( p_demux, p_dvbpsies->i_pid );
-
-        char const * psz_typedesc = ISO13818_1_Get_StreamType_Description( p_dvbpsies->i_type );
-
-        msg_Dbg( p_demux, "  * pid=%d type=0x%x %s",
-                 p_dvbpsies->i_pid, p_dvbpsies->i_type, psz_typedesc );
-
-        for( dvbpsi_descriptor_t *p_dr = p_dvbpsies->p_first_descriptor;
-             p_dr != NULL; p_dr = p_dr->p_next )
-        {
-            msg_Dbg( p_demux, "    - ES descriptor tag 0x%x",
-                     p_dr->i_tag );
-        }
-
-        const bool b_pid_inuse = ( pespid->type == TYPE_PES );
-        ts_pes_t *p_pes;
-
-        if ( !PIDSetup( p_demux, TYPE_PES, pespid, pmtpid ) )
-        {
-            msg_Warn( p_demux, "  * pid=%d type=0x%x %s (skipped)",
-                      p_dvbpsies->i_pid, p_dvbpsies->i_type, psz_typedesc );
-            continue;
-        }
-        else
-        {
-            if( b_pid_inuse ) /* pes will point to a temp */
-            {
-                p_pes = ts_pes_New( p_demux, p_pmt );
-                if( !p_pes )
-                {
-                    PIDRelease( p_demux, pespid );
-                    continue;
-                }
-            }
-            else  /* pes will point to the new one allocated from PIDSetup */
-            {
-                p_pes = pespid->u.p_pes;
-            }
-        }
-
-        /* Add pid to the list of used ones in pmt */
-        ARRAY_APPEND( p_pmt->e_streams, pespid );
-        pespid->i_flags |= SEEN(GetPID(p_sys, p_dvbpsies->i_pid));
-
-        /* Fill p_pes es and add extra es if any */
-        FillPESFromDvbpsiES( p_demux, p_dvbpsipmt, p_dvbpsies,
-                             registration_type, p_pmt, p_pes );
-
-        /* Set description and debug */
-        if( p_pes->p_es->fmt.i_cat == UNKNOWN_ES )
-        {
-            msg_Dbg( p_demux, "   => pid %d content is *unknown*",
-                     p_dvbpsies->i_pid );
-            p_pes->p_es->fmt.psz_description = strdup( psz_typedesc );
-        }
-        else
-        {
-            msg_Dbg( p_demux, "   => pid %d has now es fcc=%4.4s",
-                     p_dvbpsies->i_pid, (char*)&p_pes->p_es->fmt.i_codec );
-        }
-
-        dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x09 );
-        if( p_dr && p_dr->i_length >= 2 )
-        {
-            msg_Dbg( p_demux, "    - ES descriptor : CA (0x9) SysID 0x%x",
-                     (p_dr->p_data[0] << 8) | p_dr->p_data[1] );
-        }
-
-        const bool b_create_es = ((p_pes->p_es->fmt.i_cat == VIDEO_ES) ||
-                                  (p_pes->p_es->fmt.i_cat == AUDIO_ES));
-
-        /* Now check and merge */
-        if( b_pid_inuse ) /* We need to compare to the existing pes/es */
+        if( !p_es->id )
         {
-            ts_pes_es_t *p_existing_es = ts_pes_Find_es( pespid->u.p_pes, p_pmt );
-            if( p_existing_es )
-            {
-                const es_format_t *ofmt = &p_existing_es->fmt;
-                const es_format_t *nfmt = &p_pes->p_es->fmt;
-
-                /* Check if we can avoid restarting that ES */
-                bool b_canreuse = es_format_IsSimilar( ofmt, nfmt );
-
-                /* Check codecs extra */
-                b_canreuse = b_canreuse &&
-                (
-                    ofmt->i_extra == nfmt->i_extra &&
-                    ( ofmt->i_extra == 0 ||
-                      memcmp( ofmt->p_extra, nfmt->p_extra, nfmt->i_extra ) == 0 )
-                );
-
-                /* Tracks must have same language */
-                b_canreuse = b_canreuse &&
-                (
-                    ( !!ofmt->psz_language == !!nfmt->psz_language ) &&
-                    ( ofmt->psz_language == NULL ||
-                      !strcmp( ofmt->psz_language, nfmt->psz_language ) )
-                );
-
-                /* Check is we have any subtitles */
-                b_canreuse = b_canreuse &&
-                ( ts_pes_Count_es( p_pes->p_es->p_extraes, false, NULL ) ==
-                  ts_pes_Count_es( p_existing_es->p_extraes, false, NULL )
-                );
-
-                if( b_canreuse )
-                {
-                    /* Just keep using previous es */
-                    ts_pes_Del( p_demux, p_pes );
-                }
-                else
-                {
-                    ts_pes_es_t *p_new = ts_pes_Extract_es( p_pes, p_pmt );
-                    ts_pes_Add_es( pespid->u.p_pes, p_new, false );
-                    assert(ts_pes_Count_es(p_pes->p_es, false, NULL) == 0);
-                    ts_pes_Del( p_demux, p_pes );
-                }
-            }
-            else /* There was no es for that program on that pid, merge in */
+            if( !p_es->fmt.i_group )
+                p_es->fmt.i_group = p_es->p_program->i_number;
+            p_es->id = es_out_Add( p_demux->out, &p_es->fmt );
+            if( p_parent_es ) /* Set Extra ES group and original ID */
             {
-                assert(ts_pes_Count_es(pespid->u.p_pes->p_es, false, NULL)); /* Used by another program */
-                ts_pes_es_t *p_new = ts_pes_Extract_es( p_pes, p_pmt );
-                assert( p_new );
-                ts_pes_Add_es( pespid->u.p_pes, p_new, false );
-                ts_pes_Del( p_demux, p_pes );
+                if ( p_sys->b_es_id_pid ) /* pid is 13 bits */
+                    p_es->fmt.i_id = (p_sys->i_next_extraid++ << 13) | p_parent_es->fmt.i_id;
+                p_es->fmt.i_group = p_parent_es->fmt.i_group;
             }
+            p_sys->i_pmt_es++;
         }
-        /* Nothing to do, pes is now just set */
-        if( b_create_es )
-            AddAndCreateES( p_demux, pespid, false );
-    }
-
-    /* Set CAM descrambling */
-    if( !ProgramIsSelected( p_sys, p_pmt->i_number ) )
-    {
-        dvbpsi_pmt_delete( p_dvbpsipmt );
-    }
-    else if( stream_Control( p_sys->stream, STREAM_SET_PRIVATE_ID_CA,
-                             p_dvbpsipmt ) != VLC_SUCCESS )
-    {
-        if ( p_sys->arib.e_mode == ARIBMODE_ENABLED && !p_sys->arib.b25stream )
-        {
-            p_sys->arib.b25stream = stream_FilterNew( p_demux->s, "aribcam" );
-            p_sys->stream = ( p_sys->arib.b25stream ) ? p_sys->arib.b25stream : p_demux->s;
-            if (!p_sys->arib.b25stream)
-                dvbpsi_pmt_delete( p_dvbpsipmt );
-        } else dvbpsi_pmt_delete( p_dvbpsipmt );
-    }
-
-     /* Add arbitrary PID from here */
-    if ( p_sys->b_atsc_eas && p_pmt->e_streams.i_size )
-    {
-        ts_pid_t *easpid = GetPID(p_sys, SCTE18_SI_BASE_PID);
-        if ( PIDSetup( p_demux, TYPE_PES, easpid, pmtpid ) )
-        {
-            ARRAY_APPEND( p_pmt->e_streams, easpid );
-            ts_pes_t *p_easpes = easpid->u.p_pes;
-            p_easpes->data_type = TS_ES_DATA_TABLE_SECTION;
-            p_easpes->p_es->fmt.i_codec = VLC_CODEC_SCTE_18;
-            p_easpes->p_es->fmt.i_cat = SPU_ES;
-            p_easpes->p_es->fmt.i_id = SCTE18_SI_BASE_PID;
-            p_easpes->p_es->fmt.i_group = p_pmt->i_number;
-            p_easpes->p_es->fmt.psz_description = strdup(SCTE18_DESCRIPTION);
-            p_easpes->b_always_receive = true;
-            ts_sections_processor_Add( &p_easpes->p_sections_proc,
-                                       SCTE18_TABLE_ID, 0x00,
-                                       false, SCTE18_Section_Handler );
-            msg_Dbg( p_demux, "  * pid=%d listening for EAS events", easpid->i_pid );
-        }
-    }
-
-    /* Decref or clean now unused es */
-    for( int i = 0; i < old_es_rm.i_size; i++ )
-        PIDRelease( p_demux, old_es_rm.p_elems[i] );
-    ARRAY_RESET( old_es_rm );
-
-    UpdatePESFilters( p_demux, p_sys->b_es_all );
-
-    if( !p_sys->b_trust_pcr )
-    {
-        int i_cand = FindPCRCandidate( p_pmt );
-        p_pmt->i_pid_pcr = i_cand;
-        p_pmt->pcr.b_disable = true;
-        msg_Warn( p_demux, "PCR not trusted for program %d, set up workaround using pid %d",
-                  p_pmt->i_number, i_cand );
-    }
-
-    /* Probe Boundaries */
-    if( p_sys->b_canfastseek && p_pmt->i_last_dts == -1 )
-    {
-        p_pmt->i_last_dts = 0;
-        ProbeStart( p_demux, p_pmt->i_number );
-        ProbeEnd( p_demux, p_pmt->i_number );
-    }
-}
-
-static int PATCheck( demux_t *p_demux, dvbpsi_pat_t *p_pat )
-{
-    /* Some Dreambox streams have all PMT set to same pid */
-    int i_prev_pid = -1;
-    for( dvbpsi_pat_program_t * p_program = p_pat->p_first_program;
-         p_program != NULL;
-         p_program = p_program->p_next )
-    {
-        if( p_program->i_pid == i_prev_pid )
-        {
-            msg_Warn( p_demux, "PAT check failed: duplicate program pid %d", i_prev_pid );
-            return VLC_EGENERIC;
-        }
-        i_prev_pid = p_program->i_pid;
+        DoCreateES( p_demux, p_es->p_extraes, p_es );
     }
-    return VLC_SUCCESS;
 }
 
-static void PATCallBack( void *data, dvbpsi_pat_t *p_dvbpsipat )
+void AddAndCreateES( demux_t *p_demux, ts_pid_t *pid, bool b_create_delayed )
 {
-    demux_t              *p_demux = data;
-    demux_sys_t          *p_sys = p_demux->p_sys;
-    dvbpsi_pat_program_t *p_program;
-    ts_pid_t             *patpid = GetPID(p_sys, 0);
-    ts_pat_t             *p_pat = GetPID(p_sys, 0)->u.p_pat;
-
-    patpid->i_flags |= FLAG_SEEN;
-
-    msg_Dbg( p_demux, "PATCallBack called" );
-
-    if(unlikely( GetPID(p_sys, 0)->type != TYPE_PAT ))
-    {
-        msg_Warn( p_demux, "PATCallBack called on invalid pid" );
-        return;
-    }
-
-    if( ( p_pat->i_version != -1 &&
-            ( !p_dvbpsipat->b_current_next ||
-              p_dvbpsipat->i_version == p_pat->i_version ) ) ||
-        ( p_pat->i_ts_id != -1 && p_dvbpsipat->i_ts_id != p_pat->i_ts_id ) ||
-        p_sys->b_user_pmt || PATCheck( p_demux, p_dvbpsipat ) )
-    {
-        dvbpsi_pat_delete( p_dvbpsipat );
-        return;
-    }
-
-    msg_Dbg( p_demux, "new PAT ts_id=%d version=%d current_next=%d",
-             p_dvbpsipat->i_ts_id, p_dvbpsipat->i_version, p_dvbpsipat->b_current_next );
+    demux_sys_t  *p_sys = p_demux->p_sys;
 
-    /* Save old programs array */
-    DECL_ARRAY(ts_pid_t *) old_pmt_rm;
-    old_pmt_rm.i_alloc = p_pat->programs.i_alloc;
-    old_pmt_rm.i_size = p_pat->programs.i_size;
-    old_pmt_rm.p_elems = p_pat->programs.p_elems;
-    ARRAY_INIT(p_pat->programs);
+    if( b_create_delayed )
+        p_sys->es_creation = CREATE_ES;
 
-    /* now create programs */
-    for( p_program = p_dvbpsipat->p_first_program; p_program != NULL;
-         p_program = p_program->p_next )
+    if( pid && p_sys->es_creation == CREATE_ES )
     {
-        msg_Dbg( p_demux, "  * number=%d pid=%d", p_program->i_number,
-                 p_program->i_pid );
-        if( p_program->i_number == 0 )
-            continue;
-
-        ts_pid_t *pmtpid = GetPID(p_sys, p_program->i_pid);
-
-        ValidateDVBMeta( p_demux, p_program->i_pid );
-
-        bool b_existing = (pmtpid->type == TYPE_PMT);
-        /* create or temporary incref pid */
-        if( !PIDSetup( p_demux, TYPE_PMT, pmtpid, patpid ) )
-        {
-            msg_Warn( p_demux, "  * number=%d pid=%d (ignored)", p_program->i_number,
-                     p_program->i_pid );
-            continue;
-        }
-
-        if( !b_existing || pmtpid->u.p_pmt->i_number != p_program->i_number )
-        {
-            if( b_existing && pmtpid->u.p_pmt->i_number != p_program->i_number )
-                dvbpsi_pmt_detach(pmtpid->u.p_pmt->handle);
-
-            if( !dvbpsi_pmt_attach( pmtpid->u.p_pmt->handle, p_program->i_number, PMTCallBack, p_demux ) )
-                msg_Err( p_demux, "PATCallback failed attaching PMTCallback to program %d",
-                         p_program->i_number );
-        }
-
-        pmtpid->u.p_pmt->i_number = p_program->i_number;
-
-        ARRAY_APPEND( p_pat->programs, pmtpid );
+        DoCreateES( p_demux, pid->u.p_pes->p_es, NULL );
 
-        /* Now select PID at access level */
-        if( p_sys->programs.i_size == 0 ||
-            ProgramIsSelected( p_sys, p_program->i_number ) )
+        /* Update the default program == first created ES group */
+        if( p_sys->b_default_selection && p_sys->programs.i_size > 0)
         {
-            if( p_sys->programs.i_size == 0 )
-            {
-                msg_Dbg( p_demux, "temporary receiving program %d", p_program->i_number );
-                p_sys->b_default_selection = true;
-                ARRAY_APPEND( p_sys->programs, p_program->i_number );
-            }
-
-            if( SetPIDFilter( p_sys, pmtpid, true ) )
-                p_sys->b_access_control = false;
-            else if ( p_sys->es_creation == DELAY_ES )
-                p_sys->es_creation = CREATE_ES;
+            p_sys->b_default_selection = false;
+            const int i_first_program = pid->u.p_pes->p_es->p_program->i_number;
+            if( p_sys->programs.p_elems[0] != i_first_program )
+                p_sys->programs.p_elems[0] = i_first_program;
+            msg_Dbg( p_demux, "Default program is %d", i_first_program );
         }
     }
-    p_pat->i_version = p_dvbpsipat->i_version;
-    p_pat->i_ts_id = p_dvbpsipat->i_ts_id;
-
-    for(int i=0; i<old_pmt_rm.i_size; i++)
-    {
-        /* decref current or release now unreferenced */
-        PIDRelease( p_demux, old_pmt_rm.p_elems[i] );
-    }
-    ARRAY_RESET(old_pmt_rm);
-
-    dvbpsi_pat_delete( p_dvbpsipat );
-}
-
-static inline bool handle_Init( demux_t *p_demux, dvbpsi_t **handle )
-{
-    *handle = dvbpsi_new( &dvbpsi_messages, DVBPSI_MSG_DEBUG );
-    if( !*handle )
-        return false;
-    (*handle)->p_sys = (void *) p_demux;
-    return true;
-}
-
-static ts_pat_t *ts_pat_New( demux_t *p_demux )
-{
-    ts_pat_t *pat = malloc( sizeof( ts_pat_t ) );
-    if( !pat )
-        return NULL;
-
-    if( !handle_Init( p_demux, &pat->handle ) )
-    {
-        free( pat );
-        return NULL;
-    }
-
-    pat->i_version  = -1;
-    pat->i_ts_id    = -1;
-    ARRAY_INIT( pat->programs );
-
-    return pat;
-}
-
-static void ts_pat_Del( demux_t *p_demux, ts_pat_t *pat )
-{
-    if( dvbpsi_decoder_present( pat->handle ) )
-        dvbpsi_pat_detach( pat->handle );
-    dvbpsi_delete( pat->handle );
-    for( int i=0; i<pat->programs.i_size; i++ )
-        PIDRelease( p_demux, pat->programs.p_elems[i] );
-    ARRAY_RESET( pat->programs );
-    free( pat );
-}
-
-static ts_pmt_t *ts_pmt_New( demux_t *p_demux )
-{
-    ts_pmt_t *pmt = malloc( sizeof( ts_pmt_t ) );
-    if( !pmt )
-        return NULL;
-
-    if( !handle_Init( p_demux, &pmt->handle ) )
-    {
-        free( pmt );
-        return NULL;
-    }
-
-    ARRAY_INIT( pmt->e_streams );
-
-    pmt->i_version  = -1;
-    pmt->i_number   = -1;
-    pmt->i_pid_pcr  = 0x1FFF;
-    pmt->b_selected = false;
-    pmt->iod        = NULL;
-    pmt->od.i_version = -1;
-    ARRAY_INIT( pmt->od.objects );
-
-    pmt->i_last_dts = -1;
-
-    pmt->pcr.i_current = -1;
-    pmt->pcr.i_first  = -1;
-    pmt->pcr.b_disable = false;
-    pmt->pcr.i_first_dts = VLC_TS_INVALID;
-    pmt->pcr.i_pcroffset = -1;
-
-    pmt->pcr.b_fix_done = false;
-
-    return pmt;
-}
-
-static void ts_pmt_Del( demux_t *p_demux, ts_pmt_t *pmt )
-{
-    if( dvbpsi_decoder_present( pmt->handle ) )
-        dvbpsi_pmt_detach( pmt->handle );
-    dvbpsi_delete( pmt->handle );
-    for( int i=0; i<pmt->e_streams.i_size; i++ )
-        PIDRelease( p_demux, pmt->e_streams.p_elems[i] );
-    ARRAY_RESET( pmt->e_streams );
-    if( pmt->iod )
-        ODFree( pmt->iod );
-    for( int i=0; i<pmt->od.objects.i_size; i++ )
-        ODFree( pmt->od.objects.p_elems[i] );
-    ARRAY_RESET( pmt->od.objects );
-    if( pmt->i_number > -1 )
-        es_out_Control( p_demux->out, ES_OUT_DEL_GROUP, pmt->i_number );
-    free( pmt );
-}
-
-static ts_pes_es_t * ts_pes_es_New( ts_pmt_t *p_program )
-{
-    ts_pes_es_t *p_es = malloc( sizeof(*p_es) );
-    if( p_es )
-    {
-        p_es->p_program = p_program;
-        p_es->id = NULL;
-        p_es->i_sl_es_id = 0;
-        p_es->p_extraes = NULL;
-        p_es->p_next = NULL;
-        p_es->b_interlaced = false;
-        es_format_Init( &p_es->fmt, UNKNOWN_ES, 0 );
-        p_es->fmt.i_group = p_program->i_number;
-    }
-    return p_es;
-}
-
-static void ts_pes_es_Clean( demux_t *p_demux, ts_pes_es_t *p_es )
-{
-    if( p_es && p_es->id )
-    {
-        /* Ensure we don't wait for overlap hacks #14257 */
-        es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, p_es->id, false );
-        es_out_Del( p_demux->out, p_es->id );
-        p_demux->p_sys->i_pmt_es--;
-    }
-    es_format_Clean( &p_es->fmt );
-}
-
-static void ts_pes_Add_es( ts_pes_t *p_pes, ts_pes_es_t *p_es, bool b_extra )
-{
-    ts_pes_es_t **pp_es = (b_extra && p_pes->p_es) ?  /* Ensure extra has main es */
-                           &p_pes->p_es->p_extraes :
-                           &p_pes->p_es;
-    if( likely(!*pp_es) )
-    {
-        *pp_es = p_es;
-    }
-    else
-    {
-        ts_pes_es_t *p_next = (*pp_es)->p_next;
-        (*pp_es)->p_next = p_es;
-        p_es->p_next = p_next;
-    }
-}
-
-static ts_pes_es_t * ts_pes_Find_es( ts_pes_t *p_pes, const ts_pmt_t *p_pmt )
-{
-    for( ts_pes_es_t *p_es = p_pes->p_es; p_es; p_es = p_es->p_next )
-    {
-        if( p_es->p_program == p_pmt )
-            return p_es;
-    }
-    return NULL;
-}
 
-static ts_pes_es_t * ts_pes_Extract_es( ts_pes_t *p_pes, const ts_pmt_t *p_pmt )
-{
-    ts_pes_es_t **pp_prev = &p_pes->p_es;
-    for( ts_pes_es_t *p_es = p_pes->p_es; p_es; p_es = p_es->p_next )
+    if( b_create_delayed )
     {
-        if( p_es->p_program == p_pmt )
+        ts_pat_t *p_pat = GetPID(p_sys, 0)->u.p_pat;
+        for( int i=0; i< p_pat->programs.i_size; i++ )
         {
-            *pp_prev = p_es->p_next;
-            p_es->p_next = NULL;
-            return p_es;
+            ts_pmt_t *p_pmt = p_pat->programs.p_elems[i]->u.p_pmt;
+            for( int j=0; j<p_pmt->e_streams.i_size; j++ )
+                DoCreateES( p_demux, p_pmt->e_streams.p_elems[j]->u.p_pes->p_es, NULL );
         }
-        pp_prev = &p_es->p_next;
-    }
-    return NULL;
-}
-
-static size_t ts_pes_Count_es( const ts_pes_es_t *p_es, bool b_active, const ts_pmt_t *p_pmt )
-{
-    size_t i=0;
-    for( ; p_es; p_es = p_es->p_next )
-    {
-        i += ( b_active ) ? !!p_es->id : ( ( !p_pmt || p_pmt == p_es->p_program ) ? 1 : 0 );
-        i += ts_pes_Count_es( p_es->p_extraes, b_active, p_pmt );
-    }
-    return i;
-}
-
-static void ts_pes_ChainDelete_es( demux_t *p_demux, ts_pes_es_t *p_es )
-{
-    while( p_es )
-    {
-        ts_pes_es_t *p_next = p_es->p_next;
-        ts_pes_ChainDelete_es( p_demux, p_es->p_extraes );
-        ts_pes_es_Clean( p_demux, p_es );
-        free( p_es );
-        p_es = p_next;
-    }
-}
-
-static ts_pes_t *ts_pes_New( demux_t *p_demux, ts_pmt_t *p_program )
-{
-    VLC_UNUSED(p_demux);
-    ts_pes_t *pes = malloc( sizeof( ts_pes_t ) );
-    if( !pes )
-        return NULL;
-
-    pes->p_es = ts_pes_es_New( p_program );
-    if( !pes->p_es )
-    {
-        free( pes );
-        return NULL;
-    }
-    pes->i_stream_type = 0;
-    pes->data_type = TS_ES_DATA_PES;
-    pes->i_data_size = 0;
-    pes->i_data_gathered = 0;
-    pes->p_data = NULL;
-    pes->pp_last = &pes->p_data;
-    pes->b_always_receive = false;
-    pes->p_sections_proc = NULL;
-    pes->p_prepcr_outqueue = NULL;
-    pes->sl.p_data = NULL;
-    pes->sl.pp_last = &pes->sl.p_data;
-
-    return pes;
-}
-
-static void ts_pes_Del( demux_t *p_demux, ts_pes_t *pes )
-{
-    ts_pes_ChainDelete_es( p_demux, pes->p_es );
-
-    if( pes->p_data )
-        block_ChainRelease( pes->p_data );
-
-    if( pes->p_sections_proc )
-        ts_sections_processor_ChainDelete( pes->p_sections_proc );
-
-    if( pes->p_prepcr_outqueue )
-        block_ChainRelease( pes->p_prepcr_outqueue );
-
-    free( pes );
-}
-
-static ts_psi_t *ts_psi_New( demux_t *p_demux )
-{
-    ts_psi_t *psi = malloc( sizeof( ts_psi_t ) );
-    if( !psi )
-        return NULL;
-
-    if( !handle_Init( p_demux, &psi->handle ) )
-    {
-        free( psi );
-        return NULL;
     }
 
-    psi->i_version  = -1;
-
-    return psi;
-}
-
-static void ts_psi_Del( demux_t *p_demux, ts_psi_t *psi )
-{
-    VLC_UNUSED(p_demux);
-    if( dvbpsi_decoder_present( psi->handle ) )
-        dvbpsi_DetachDemux( psi->handle );
-    dvbpsi_delete( psi->handle );
-    free( psi );
+    UpdatePESFilters( p_demux, p_sys->b_es_all );
 }
diff --git a/modules/demux/mpeg/ts.h b/modules/demux/mpeg/ts.h
index 79427530986f..e55a02c126f7 100644
--- a/modules/demux/mpeg/ts.h
+++ b/modules/demux/mpeg/ts.h
@@ -20,6 +20,114 @@
 #ifndef VLC_TS_H
 #define VLC_TS_H
 
-typedef struct ts_pid_t ts_pid_t;
+#ifdef HAVE_ARIBB24
+    typedef struct arib_instance_t arib_instance_t;
+#endif
+typedef struct csa_t csa_t;
+
+#define TS_USER_PMT_NUMBER (0)
+
+typedef enum arib_modes_e
+{
+    ARIBMODE_AUTO = -1,
+    ARIBMODE_DISABLED = 0,
+    ARIBMODE_ENABLED = 1
+} arib_modes_e;
+
+typedef struct
+{
+    int i_service;
+} vdr_info_t;
+
+struct demux_sys_t
+{
+    stream_t   *stream;
+    bool        b_canseek;
+    bool        b_canfastseek;
+    vlc_mutex_t     csa_lock;
+
+    /* TS packet size (188, 192, 204) */
+    unsigned    i_packet_size;
+
+    /* Additional TS packet header size (BluRay TS packets have 4-byte header before sync byte) */
+    unsigned    i_packet_header_size;
+
+    /* how many TS packet we read at once */
+    unsigned    i_ts_read;
+
+    bool        b_force_seek_per_percent;
+
+    bool        b_atsc_eas;
+    struct
+    {
+        arib_modes_e e_mode;
+#ifdef HAVE_ARIBB24
+        arib_instance_t *p_instance;
+#endif
+        stream_t     *b25stream;
+    } arib;
+
+    /* All pid */
+    ts_pid_list_t pids;
+
+    bool        b_user_pmt;
+    int         i_pmt_es;
+    bool        b_es_all; /* If we need to return all es/programs */
+
+    enum
+    {
+        NO_ES, /* for preparse */
+        DELAY_ES,
+        CREATE_ES
+    } es_creation;
+    #define PREPARSING p_sys->es_creation == NO_ES
+
+    /* */
+    bool        b_es_id_pid;
+    uint16_t    i_next_extraid;
+
+    csa_t       *csa;
+    int         i_csa_pkt_size;
+    bool        b_split_es;
+
+    bool        b_trust_pcr;
+
+    /* */
+    bool        b_access_control;
+    bool        b_end_preparse;
+
+    /* */
+    bool        b_dvb_meta;
+    int64_t     i_tdt_delta;
+    int64_t     i_dvb_start;
+    int64_t     i_dvb_length;
+    bool        b_broken_charset; /* True if broken encoding is used in EPG/SDT */
+
+    /* Selected programs */
+    DECL_ARRAY( int ) programs; /* List of selected/access-filtered programs */
+    bool        b_default_selection; /* True if set by default to first pmt seen (to get data from filtered access) */
+
+    struct
+    {
+        mtime_t i_first_dts;     /* first dts encountered for the stream */
+        int     i_timesourcepid; /* which pid we saved the dts from */
+        enum { PAT_WAITING = 0, PAT_MISSING, PAT_FIXTRIED } status; /* set if we haven't seen PAT within MIN_PAT_INTERVAL */
+    } patfix;
+
+    vdr_info_t  vdr;
+
+    /* */
+    bool        b_start_record;
+};
+
+bool ProgramIsSelected( demux_sys_t *, uint16_t i_pgrm );
+
+void UpdatePESFilters( demux_t *p_demux, bool b_all );
+
+int ProbeStart( demux_t *p_demux, int i_program );
+int ProbeEnd( demux_t *p_demux, int i_program );
+
+void AddAndCreateES( demux_t *p_demux, ts_pid_t *pid, bool b_create_delayed );
+int FindPCRCandidate( ts_pmt_t *p_pmt );
 
 #endif
diff --git a/modules/demux/mpeg/ts_hotfixes.c b/modules/demux/mpeg/ts_hotfixes.c
new file mode 100644
index 000000000000..c485b1ba37c7
--- /dev/null
+++ b/modules/demux/mpeg/ts_hotfixes.c
@@ -0,0 +1,354 @@
+/*****************************************************************************
+ * ts_hotfixes.c : MPEG PMT/PAT less streams fixups
+ *****************************************************************************
+ * Copyright (C) 2014-2016 - VideoLAN Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_demux.h>
+#include <vlc_es.h>
+
+#ifndef _DVBPSI_DVBPSI_H_
+ #include <dvbpsi/dvbpsi.h>
+#endif
+#include <dvbpsi/descriptor.h>
+#include <dvbpsi/pat.h>
+#include <dvbpsi/pmt.h>
+
+#include "../../mux/mpeg/streams.h"
+#include "../../mux/mpeg/tsutil.h"
+#include "../../mux/mpeg/tables.h"
+
+#include "timestamps.h"
+#include "pes.h"
+
+#include "ts_streams.h"
+#include "ts_psi.h"
+#include "ts_pid.h"
+#include "ts_streams_private.h"
+#include "ts.h"
+#include "ts_hotfixes.h"
+
+#include <assert.h>
+
+void ProbePES( demux_t *p_demux, ts_pid_t *pid, const uint8_t *p_pesstart, size_t i_data, bool b_adaptfield )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    const uint8_t *p_pes = p_pesstart;
+    pid->probed.i_type = -1;
+
+    if( b_adaptfield )
+    {
+        if ( i_data < 2 )
+            return;
+
+        uint8_t len = *p_pes;
+        p_pes++; i_data--;
+
+        if(len == 0)
+        {
+            p_pes++; i_data--;/* stuffing */
+        }
+        else
+        {
+            if( i_data < len )
+                return;
+            if( len >= 7 && (p_pes[1] & 0x10) )
+                pid->probed.i_pcr_count++;
+            p_pes += len;
+            i_data -= len;
+        }
+    }
+
+    if( i_data < 9 )
+        return;
+
+    if( p_pes[0] != 0 || p_pes[1] != 0 || p_pes[2] != 1 )
+        return;
+
+    size_t i_pesextoffset = 8;
+    mtime_t i_dts = -1;
+    if( p_pes[7] & 0x80 ) // PTS
+    {
+        i_pesextoffset += 5;
+        if ( i_data < i_pesextoffset )
+            return;
+        i_dts = ExtractPESTimestamp( &p_pes[9] );
+    }
+    if( p_pes[7] & 0x40 ) // DTS
+    {
+        i_pesextoffset += 5;
+        if ( i_data < i_pesextoffset )
+            return;
+        i_dts = ExtractPESTimestamp( &p_pes[14] );
+    }
+    if( p_pes[7] & 0x20 ) // ESCR
+        i_pesextoffset += 6;
+    if( p_pes[7] & 0x10 ) // ESrate
+        i_pesextoffset += 3;
+    if( p_pes[7] & 0x08 ) // DSM
+        i_pesextoffset += 1;
+    if( p_pes[7] & 0x04 ) // CopyInfo
+        i_pesextoffset += 1;
+    if( p_pes[7] & 0x02 ) // PESCRC
+        i_pesextoffset += 2;
+
+    if ( i_data < i_pesextoffset )
+        return;
+
+     /* HeaderdataLength */
+    const size_t i_payloadoffset = 8 + 1 + p_pes[8];
+    i_pesextoffset += 1;
+
+    if ( i_data < i_pesextoffset || i_data < i_payloadoffset )
+        return;
+
+    i_data -= 8 + 1 + p_pes[8];
+
+    if( p_pes[7] & 0x01 ) // PESExt
+    {
+        size_t i_extension2_offset = 1;
+        if ( p_pes[i_pesextoffset] & 0x80 ) // private data
+            i_extension2_offset += 16;
+        if ( p_pes[i_pesextoffset] & 0x40 ) // pack
+            i_extension2_offset += 1;
+        if ( p_pes[i_pesextoffset] & 0x20 ) // seq
+            i_extension2_offset += 2;
+        if ( p_pes[i_pesextoffset] & 0x10 ) // P-STD
+            i_extension2_offset += 2;
+        if ( p_pes[i_pesextoffset] & 0x01 ) // Extension 2
+        {
+            uint8_t i_len = p_pes[i_pesextoffset + i_extension2_offset] & 0x7F;
+            i_extension2_offset += i_len;
+        }
+        if( i_data < i_extension2_offset )
+            return;
+
+        i_data -= i_extension2_offset;
+    }
+    /* (i_payloadoffset - i_pesextoffset) 0xFF stuffing */
+
+    if ( i_data < 4 )
+        return;
+
+    const uint8_t *p_data = &p_pes[i_payloadoffset];
+    /* NON MPEG audio & subpictures STREAM */
+    if(p_pes[3] == 0xBD)
+    {
+        if( !memcmp( p_data, "\x7F\xFE\x80\x01", 4 ) )
+        {
+            pid->probed.i_type = 0x06;
+            pid->probed.i_fourcc = VLC_CODEC_DTS;
+        }
+        else if( !memcmp( p_data, "\x0B\x77", 2 ) )
+        {
+            pid->probed.i_type = 0x06;
+            pid->probed.i_fourcc = VLC_CODEC_EAC3;
+        }
+    }
+    /* MPEG AUDIO STREAM */
+    else if(p_pes[3] >= 0xC0 && p_pes[3] <= 0xDF)
+    {
+        if( p_data[0] == 0xFF && (p_data[1] & 0xE0) == 0xE0 )
+        {
+            switch(p_data[1] & 18)
+            {
+            /* 10 - MPEG Version 2 (ISO/IEC 13818-3)
+               11 - MPEG Version 1 (ISO/IEC 11172-3) */
+                case 0x10:
+                    pid->probed.i_type = 0x04;
+                    break;
+                case 0x18:
+                    pid->probed.i_type = 0x03;
+                default:
+                    break;
+            }
+
+            switch(p_data[1] & 6)
+            {
+            /* 01 - Layer III
+               10 - Layer II
+               11 - Layer I */
+                case 0x06:
+                    pid->probed.i_type = 0x04;
+                    pid->probed.i_fourcc = VLC_CODEC_MPGA;
+                    break;
+                case 0x04:
+                    pid->probed.i_type = 0x04;
+                    pid->probed.i_fourcc = VLC_CODEC_MP2;
+                    break;
+                case 0x02:
+                    pid->probed.i_type = 0x04;
+                    pid->probed.i_fourcc = VLC_CODEC_MP3;
+                default:
+                    break;
+            }
+        }
+    }
+    /* VIDEO STREAM */
+    else if( p_pes[3] >= 0xE0 && p_pes[3] <= 0xEF )
+    {
+        if( !memcmp( p_data, "\x00\x00\x00\x01", 4 ) )
+        {
+            pid->probed.i_type = 0x1b;
+            pid->probed.i_fourcc = VLC_CODEC_H264;
+        }
+        else if( !memcmp( p_data, "\x00\x00\x01", 4 ) )
+        {
+            pid->probed.i_type = 0x02;
+            pid->probed.i_fourcc = VLC_CODEC_MPGV;
+        }
+    }
+
+    /* Track timestamps and flag missing PAT */
+    if( !p_sys->patfix.i_timesourcepid && i_dts > -1 )
+    {
+        p_sys->patfix.i_first_dts = i_dts;
+        p_sys->patfix.i_timesourcepid = pid->i_pid;
+    }
+    else if( p_sys->patfix.i_timesourcepid == pid->i_pid && i_dts > -1 &&
+             p_sys->patfix.status == PAT_WAITING )
+    {
+        if( i_dts - p_sys->patfix.i_first_dts > TO_SCALE(MIN_PAT_INTERVAL) )
+            p_sys->patfix.status = PAT_MISSING;
+    }
+
+}
+
+static void BuildPATCallback( void *p_opaque, block_t *p_block )
+{
+    ts_pid_t *pat_pid = (ts_pid_t *) p_opaque;
+    dvbpsi_packet_push( pat_pid->u.p_pat->handle, p_block->p_buffer );
+}
+
+static void BuildPMTCallback( void *p_opaque, block_t *p_block )
+{
+    ts_pid_t *program_pid = (ts_pid_t *) p_opaque;
+    assert(program_pid->type == TYPE_PMT);
+    while( p_block )
+    {
+        dvbpsi_packet_push( program_pid->u.p_pmt->handle,
+                            p_block->p_buffer );
+        p_block = p_block->p_next;
+    }
+}
+
+void MissingPATPMTFixup( demux_t *p_demux )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    int i_program_number = 1234;
+    int i_program_pid = 1337;
+    int i_pcr_pid = 0x1FFF;
+    int i_num_pes = 0;
+
+    ts_pid_t *p_program_pid = GetPID( p_sys, i_program_pid );
+    if( SEEN(p_program_pid) )
+    {
+        /* Find a free one */
+        for( i_program_pid = MIN_ES_PID;
+             i_program_pid <= MAX_ES_PID && SEEN(p_program_pid);
+             i_program_pid++ )
+        {
+            p_program_pid = GetPID( p_sys, i_program_pid );
+        }
+    }
+
+    const ts_pid_t *p_pid = NULL;
+    ts_pid_next_context_t pidnextctx = ts_pid_NextContextInitValue;
+    while( (p_pid = ts_pid_Next( &p_sys->pids, &pidnextctx )) )
+    {
+        if( !SEEN(p_pid) || p_pid->probed.i_type == -1 )
+            continue;
+
+        if( i_pcr_pid == 0x1FFF && ( p_pid->probed.i_type == 0x03 ||
+                                     p_pid->probed.i_pcr_count ) )
+            i_pcr_pid = p_pid->i_pid;
+
+        i_num_pes++;
+    }
+
+    if( i_num_pes == 0 )
+        return;
+
+    ts_stream_t patstream =
+    {
+        .i_pid = 0,
+        .i_continuity_counter = 0x10,
+        .b_discontinuity = false
+    };
+
+    ts_stream_t pmtprogramstream =
+    {
+        .i_pid = i_program_pid,
+        .i_continuity_counter = 0x0,
+        .b_discontinuity = false
+    };
+
+    BuildPAT( GetPID(p_sys, 0)->u.p_pat->handle,
+            &p_sys->pids.pat, BuildPATCallback,
+            0, 1,
+            &patstream,
+            1, &pmtprogramstream, &i_program_number );
+
+    /* PAT callback should have been triggered */
+    if( p_program_pid->type != TYPE_PMT )
+    {
+        msg_Err( p_demux, "PAT creation failed" );
+        return;
+    }
+
+    struct esstreams_t
+    {
+        pes_stream_t pes;
+        ts_stream_t ts;
+    };
+    es_format_t esfmt = {0};
+    struct esstreams_t *esstreams = calloc( i_num_pes, sizeof(struct esstreams_t) );
+    pes_mapped_stream_t *mapped = calloc( i_num_pes, sizeof(pes_mapped_stream_t) );
+    if( esstreams && mapped )
+    {
+        int j=0;
+        for( int i=0; i<p_sys->pids.i_all; i++ )
+        {
+            const ts_pid_t *p_pid = p_sys->pids.pp_all[i];
+
+            if( !SEEN(p_pid) ||
+                p_pid->probed.i_type == -1 )
+                continue;
+
+            esstreams[j].pes.i_codec = p_pid->probed.i_fourcc;
+            esstreams[j].pes.i_stream_type = p_pid->probed.i_type;
+            esstreams[j].ts.i_pid = p_pid->i_pid;
+            mapped[j].pes = &esstreams[j].pes;
+            mapped[j].ts = &esstreams[j].ts;
+            mapped[j].fmt = &esfmt;
+            j++;
+        }
+
+        BuildPMT( GetPID(p_sys, 0)->u.p_pat->handle, VLC_OBJECT(p_demux),
+                p_program_pid, BuildPMTCallback,
+                0, 1,
+                i_pcr_pid,
+                NULL,
+                1, &pmtprogramstream, &i_program_number,
+                i_num_pes, mapped );
+    }
+    free(esstreams);
+    free(mapped);
+}
diff --git a/modules/demux/mpeg/ts_hotfixes.h b/modules/demux/mpeg/ts_hotfixes.h
new file mode 100644
index 000000000000..25faf05b1fa4
--- /dev/null
+++ b/modules/demux/mpeg/ts_hotfixes.h
@@ -0,0 +1,27 @@
+/*****************************************************************************
+ * ts_hotfixes.h : MPEG PMT/PAT less streams fixups
+ *****************************************************************************
+ * Copyright (C) 2014-2016 - VideoLAN Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifndef VLC_TS_HOTFIXES_H
+#define VLC_TS_HOTFIXES_H
+
+#define MIN_PAT_INTERVAL CLOCK_FREQ // DVB is 500ms
+
+void ProbePES( demux_t *p_demux, ts_pid_t *pid, const uint8_t *p_pesstart, size_t i_data, bool b_adaptfield );
+void MissingPATPMTFixup( demux_t *p_demux );
+
+#endif
diff --git a/modules/demux/mpeg/ts_pid.c b/modules/demux/mpeg/ts_pid.c
new file mode 100644
index 000000000000..41a7b6f1b059
--- /dev/null
+++ b/modules/demux/mpeg/ts_pid.c
@@ -0,0 +1,268 @@
+/*****************************************************************************
+ * ts_pid.c: Transport Stream input module for VLC.
+ *****************************************************************************
+ * Copyright (C) 2004-2016 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_demux.h>
+
+#include "ts_pid.h"
+#include "ts_streams.h"
+
+#include "ts.h"
+
+#include <assert.h>
+
+#define PID_ALLOC_CHUNK 16
+
+void ts_pid_list_Init( ts_pid_list_t *p_list )
+{
+    p_list->dummy.i_pid = 8191;
+    p_list->dummy.i_flags = FLAG_SEEN;
+    p_list->pp_all = NULL;
+    p_list->i_all = 0;
+    p_list->i_all_alloc = 0;
+    p_list->i_last_pid = 0;
+    p_list->p_last = NULL;
+}
+
+void ts_pid_list_Release( demux_t *p_demux, ts_pid_list_t *p_list )
+{
+    for( int i = 0; i < p_list->i_all; i++ )
+    {
+        ts_pid_t *pid = p_list->pp_all[i];
+#ifndef NDEBUG
+        if( pid->type != TYPE_FREE )
+            msg_Err( p_demux, "PID %d type %d not freed refcount %d", pid->i_pid, pid->type, pid->i_refcount );
+#endif
+        free( pid );
+    }
+    free( p_list->pp_all );
+}
+
+ts_pid_t * ts_pid_Get( ts_pid_list_t *p_list, uint16_t i_pid )
+{
+    switch( i_pid )
+    {
+        case 0:
+            return &p_list->pat;
+        case 0x1FFF:
+            return &p_list->dummy;
+        default:
+            if( p_list->i_last_pid == i_pid )
+                return p_list->p_last;
+        break;
+    }
+
+    for( int i=0; i < p_list->i_all; i++ )
+    {
+        if( p_list->pp_all[i]->i_pid == i_pid )
+        {
+            p_list->p_last = p_list->pp_all[i];
+            p_list->i_last_pid = i_pid;
+            return p_list->p_last;
+        }
+    }
+
+    if( p_list->i_all >= p_list->i_all_alloc )
+    {
+        ts_pid_t **p_realloc = realloc( p_list->pp_all,
+                                       (p_list->i_all_alloc + PID_ALLOC_CHUNK) * sizeof(ts_pid_t *) );
+        if( !p_realloc )
+        {
+            abort();
+            //return NULL;
+        }
+        p_list->pp_all = p_realloc;
+        p_list->i_all_alloc += PID_ALLOC_CHUNK;
+    }
+
+    ts_pid_t *p_pid = calloc( 1, sizeof(*p_pid) );
+    if( !p_pid )
+    {
+        abort();
+        //return NULL;
+    }
+
+    p_pid->i_pid = i_pid;
+    p_list->pp_all[p_list->i_all++] = p_pid;
+
+    p_list->p_last = p_pid;
+    p_list->i_last_pid = i_pid;
+
+    return p_pid;
+}
+
+ts_pid_t * ts_pid_Next( ts_pid_list_t *p_list, ts_pid_next_context_t *p_ctx )
+{
+    if( likely(p_list->i_all && p_ctx) )
+    {
+        if( p_ctx->i_pos < p_list->i_all )
+            return p_list->pp_all[p_ctx->i_pos++];
+    }
+    return NULL;
+}
+
+static void PIDReset( ts_pid_t *pid )
+{
+    assert(pid->i_refcount == 0);
+    pid->i_cc       = 0xff;
+    pid->i_flags    &= ~FLAG_SCRAMBLED;
+    pid->type = TYPE_FREE;
+}
+
+bool PIDSetup( demux_t *p_demux, ts_pid_type_t i_type, ts_pid_t *pid, ts_pid_t *p_parent )
+{
+    if( pid == p_parent || pid->i_pid == 0x1FFF )
+        return false;
+
+    if( pid->i_refcount == 0 )
+    {
+        assert( pid->type == TYPE_FREE );
+        switch( i_type )
+        {
+        case TYPE_FREE: /* nonsense ?*/
+            PIDReset( pid );
+            return true;
+
+        case TYPE_PAT:
+            PIDReset( pid );
+            pid->u.p_pat = ts_pat_New( p_demux );
+            if( !pid->u.p_pat )
+                return false;
+            break;
+
+        case TYPE_PMT:
+            PIDReset( pid );
+            pid->u.p_pmt = ts_pmt_New( p_demux );
+            if( !pid->u.p_pmt )
+                return false;
+            break;
+
+        case TYPE_PES:
+            PIDReset( pid );
+            pid->u.p_pes = ts_pes_New( p_demux, p_parent->u.p_pmt );
+            if( !pid->u.p_pes )
+                return false;
+            break;
+
+        case TYPE_SDT:
+        case TYPE_TDT:
+        case TYPE_EIT:
+            PIDReset( pid );
+            pid->u.p_psi = ts_psi_New( p_demux );
+            if( !pid->u.p_psi )
+                return false;
+            break;
+
+        default:
+            assert(false);
+            break;
+        }
+
+        pid->i_refcount++;
+        pid->type = i_type;
+    }
+    else if( pid->type == i_type && pid->i_refcount < UINT16_MAX )
+    {
+        pid->i_refcount++;
+    }
+    else
+    {
+        if( pid->type != TYPE_FREE )
+            msg_Warn( p_demux, "Tried to redeclare pid %d with another type", pid->i_pid );
+        return false;
+    }
+
+    return true;
+}
+
+void PIDRelease( demux_t *p_demux, ts_pid_t *pid )
+{
+    if( pid->i_refcount == 0 )
+    {
+        assert( pid->type == TYPE_FREE );
+        return;
+    }
+    else if( pid->i_refcount == 1 )
+    {
+        pid->i_refcount--;
+    }
+    else if( pid->i_refcount > 1 )
+    {
+        assert( pid->type != TYPE_FREE && pid->type != TYPE_PAT );
+        pid->i_refcount--;
+    }
+
+    if( pid->i_refcount == 0 )
+    {
+        switch( pid->type )
+        {
+        default:
+        case TYPE_FREE: /* nonsense ?*/
+            assert( pid->type != TYPE_FREE );
+            break;
+
+        case TYPE_PAT:
+            ts_pat_Del( p_demux, pid->u.p_pat );
+            pid->u.p_pat = NULL;
+            break;
+
+        case TYPE_PMT:
+            ts_pmt_Del( p_demux, pid->u.p_pmt );
+            pid->u.p_pmt = NULL;
+            break;
+
+        case TYPE_PES:
+            ts_pes_Del( p_demux, pid->u.p_pes );
+            pid->u.p_pes = NULL;
+            break;
+
+        case TYPE_SDT:
+        case TYPE_TDT:
+        case TYPE_EIT:
+            ts_psi_Del( p_demux, pid->u.p_psi );
+            pid->u.p_psi = NULL;
+            break;
+        }
+
+        SetPIDFilter( p_demux->p_sys, pid, false );
+        PIDReset( pid );
+    }
+}
+
+int UpdateHWFilter( demux_sys_t *p_sys, ts_pid_t *p_pid )
+{
+    if( !p_sys->b_access_control )
+        return VLC_EGENERIC;
+
+    return stream_Control( p_sys->stream, STREAM_SET_PRIVATE_ID_STATE,
+                           p_pid->i_pid, !!(p_pid->i_flags & FLAG_FILTERED) );
+}
+
+int SetPIDFilter( demux_sys_t *p_sys, ts_pid_t *p_pid, bool b_selected )
+{
+    if( b_selected )
+        p_pid->i_flags |= FLAG_FILTERED;
+    else
+        p_pid->i_flags &= ~FLAG_FILTERED;
+
+    return UpdateHWFilter( p_sys, p_pid );
+}
diff --git a/modules/demux/mpeg/ts_pid.h b/modules/demux/mpeg/ts_pid.h
new file mode 100644
index 000000000000..3a94a0a6d81d
--- /dev/null
+++ b/modules/demux/mpeg/ts_pid.h
@@ -0,0 +1,118 @@
+/*****************************************************************************
+ * ts_pid.h: Transport Stream input module for VLC.
+ *****************************************************************************
+ * Copyright (C) 2004-2016 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifndef VLC_TS_PID_H
+#define VLC_TS_PID_H
+
+typedef struct ts_pid_t ts_pid_t;
+
+#define MIN_ES_PID 4    /* Should be 32.. broken muxers */
+#define MAX_ES_PID 8190
+
+#include "ts_streams.h"
+
+typedef enum
+{
+    TYPE_FREE = 0,
+    TYPE_PAT,
+    TYPE_PMT,
+    TYPE_PES,
+    TYPE_SDT,
+    TYPE_TDT,
+    TYPE_EIT,
+} ts_pid_type_t;
+
+enum
+{
+    FLAGS_NONE = 0,
+    FLAG_SEEN  = 1,
+    FLAG_SCRAMBLED = 2,
+    FLAG_FILTERED = 4
+};
+
+#define SEEN(x) ((x)->i_flags & FLAG_SEEN)
+#define SCRAMBLED(x) ((x).i_flags & FLAG_SCRAMBLED)
+
+struct ts_pid_t
+{
+    uint16_t    i_pid;
+
+    uint8_t     i_flags;
+    uint8_t     i_cc;   /* countinuity counter */
+    uint8_t     type;
+
+    uint16_t    i_refcount;
+
+    /* */
+    union
+    {
+        ts_pat_t    *p_pat;
+        ts_pmt_t    *p_pmt;
+        ts_pes_t    *p_pes;
+        ts_psi_t    *p_psi;
+    } u;
+
+    struct
+    {
+        vlc_fourcc_t i_fourcc;
+        int i_type;
+        int i_pcr_count;
+    } probed;
+
+};
+
+typedef struct ts_pid_list_t
+{
+    ts_pid_t   pat;
+    ts_pid_t   dummy;
+    /* all non commons ones, dynamically allocated */
+    ts_pid_t **pp_all;
+    int        i_all;
+    int        i_all_alloc;
+    /* last recently used */
+    uint16_t   i_last_pid;
+    ts_pid_t  *p_last;
+
+} ts_pid_list_t;
+
+/* opacified pid list */
+void ts_pid_list_Init( ts_pid_list_t * );
+void ts_pid_list_Release( demux_t *, ts_pid_list_t * );
+
+/* creates missing pid on the fly */
+ts_pid_t * ts_pid_Get( ts_pid_list_t *, uint16_t i_pid );
+
+/* returns NULL on end. requires context */
+typedef struct
+{
+    int i_pos;
+} ts_pid_next_context_t;
+#define ts_pid_NextContextInitValue { 0 }
+ts_pid_t * ts_pid_Next( ts_pid_list_t *, ts_pid_next_context_t * );
+
+/* for legacy only: don't use and pass directly list reference */
+#define GetPID(p_sys, i_pid) ts_pid_Get((&(p_sys)->pids), i_pid)
+
+int UpdateHWFilter( demux_sys_t *, ts_pid_t * );
+int SetPIDFilter( demux_sys_t *, ts_pid_t *, bool b_selected );
+
+bool PIDSetup( demux_t *p_demux, ts_pid_type_t i_type, ts_pid_t *pid, ts_pid_t *p_parent );
+void PIDRelease( demux_t *p_demux, ts_pid_t *pid );
+
+
+#endif
diff --git a/modules/demux/mpeg/ts_psi.c b/modules/demux/mpeg/ts_psi.c
new file mode 100644
index 000000000000..a31715c74374
--- /dev/null
+++ b/modules/demux/mpeg/ts_psi.c
@@ -0,0 +1,1784 @@
+/*****************************************************************************
+ * ts_psi.c: Transport Stream input module for VLC.
+ *****************************************************************************
+ * Copyright (C) 2004-2016 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+
+#ifndef _DVBPSI_DVBPSI_H_
+ # include <dvbpsi/dvbpsi.h>
+#endif
+
+#include <dvbpsi/descriptor.h>
+#include <dvbpsi/pat.h>
+#include <dvbpsi/pmt.h>
+#include <dvbpsi/dr.h>
+
+#include <vlc_demux.h>
+#include <vlc_bits.h>
+
+#include "ts_streams.h"
+#include "ts_psi.h"
+#include "ts_pid.h"
+#include "ts_streams_private.h"
+#include "ts.h"
+
+#include "ts_strings.h"
+
+#include "timestamps.h"
+
+#include "../../codec/opus_header.h"
+
+#include "sections.h"
+#include "ts_sl.h"
+#include "ts_scte.h"
+
+#include <assert.h>
+
+static void PIDFillFormat( ts_pes_t *p_pes, int i_stream_type, ts_es_data_type_t * );
+
+static void ValidateDVBMeta( demux_t *p_demux, int i_pid )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    if( !p_sys->b_dvb_meta || ( i_pid != 0x11 && i_pid != 0x12 && i_pid != 0x14 ) )
+        return;
+
+    msg_Warn( p_demux, "Switching to non DVB mode" );
+
+    /* This doesn't look like a DVB stream so don't try
+     * parsing the SDT/EDT/TDT */
+
+    PIDRelease( p_demux, GetPID(p_sys, 0x11) );
+    PIDRelease( p_demux, GetPID(p_sys, 0x12) );
+    PIDRelease( p_demux, GetPID(p_sys, 0x14) );
+    p_sys->b_dvb_meta = false;
+}
+
+static int PATCheck( demux_t *p_demux, dvbpsi_pat_t *p_pat )
+{
+    /* Some Dreambox streams have all PMT set to same pid */
+    int i_prev_pid = -1;
+    for( dvbpsi_pat_program_t * p_program = p_pat->p_first_program;
+         p_program != NULL;
+         p_program = p_program->p_next )
+    {
+        if( p_program->i_pid == i_prev_pid )
+        {
+            msg_Warn( p_demux, "PAT check failed: duplicate program pid %d", i_prev_pid );
+            return VLC_EGENERIC;
+        }
+        i_prev_pid = p_program->i_pid;
+    }
+    return VLC_SUCCESS;
+}
+
+void PATCallBack( void *data, dvbpsi_pat_t *p_dvbpsipat )
+{
+    demux_t              *p_demux = data;
+    demux_sys_t          *p_sys = p_demux->p_sys;
+    dvbpsi_pat_program_t *p_program;
+    ts_pid_t             *patpid = GetPID(p_sys, 0);
+    ts_pat_t             *p_pat = GetPID(p_sys, 0)->u.p_pat;
+
+    patpid->i_flags |= FLAG_SEEN;
+
+    msg_Dbg( p_demux, "PATCallBack called" );
+
+    if(unlikely( GetPID(p_sys, 0)->type != TYPE_PAT ))
+    {
+        msg_Warn( p_demux, "PATCallBack called on invalid pid" );
+        return;
+    }
+
+    if( ( p_pat->i_version != -1 &&
+            ( !p_dvbpsipat->b_current_next ||
+              p_dvbpsipat->i_version == p_pat->i_version ) ) ||
+        ( p_pat->i_ts_id != -1 && p_dvbpsipat->i_ts_id != p_pat->i_ts_id ) ||
+        p_sys->b_user_pmt || PATCheck( p_demux, p_dvbpsipat ) )
+    {
+        dvbpsi_pat_delete( p_dvbpsipat );
+        return;
+    }
+
+    msg_Dbg( p_demux, "new PAT ts_id=%d version=%d current_next=%d",
+             p_dvbpsipat->i_ts_id, p_dvbpsipat->i_version, p_dvbpsipat->b_current_next );
+
+    /* Save old programs array */
+    DECL_ARRAY(ts_pid_t *) old_pmt_rm;
+    old_pmt_rm.i_alloc = p_pat->programs.i_alloc;
+    old_pmt_rm.i_size = p_pat->programs.i_size;
+    old_pmt_rm.p_elems = p_pat->programs.p_elems;
+    ARRAY_INIT(p_pat->programs);
+
+    /* now create programs */
+    for( p_program = p_dvbpsipat->p_first_program; p_program != NULL;
+         p_program = p_program->p_next )
+    {
+        msg_Dbg( p_demux, "  * number=%d pid=%d", p_program->i_number,
+                 p_program->i_pid );
+        if( p_program->i_number == 0 )
+            continue;
+
+        ts_pid_t *pmtpid = GetPID(p_sys, p_program->i_pid);
+
+        ValidateDVBMeta( p_demux, p_program->i_pid );
+
+        bool b_existing = (pmtpid->type == TYPE_PMT);
+        /* create or temporary incref pid */
+        if( !PIDSetup( p_demux, TYPE_PMT, pmtpid, patpid ) )
+        {
+            msg_Warn( p_demux, "  * number=%d pid=%d (ignored)", p_program->i_number,
+                     p_program->i_pid );
+            continue;
+        }
+
+        if( !b_existing || pmtpid->u.p_pmt->i_number != p_program->i_number )
+        {
+            if( b_existing && pmtpid->u.p_pmt->i_number != p_program->i_number )
+                dvbpsi_pmt_detach(pmtpid->u.p_pmt->handle);
+
+            if( !dvbpsi_pmt_attach( pmtpid->u.p_pmt->handle, p_program->i_number, PMTCallBack, p_demux ) )
+                msg_Err( p_demux, "PATCallback failed attaching PMTCallback to program %d",
+                         p_program->i_number );
+        }
+
+        pmtpid->u.p_pmt->i_number = p_program->i_number;
+
+        ARRAY_APPEND( p_pat->programs, pmtpid );
+
+        /* Now select PID at access level */
+        if( p_sys->programs.i_size == 0 ||
+            ProgramIsSelected( p_sys, p_program->i_number ) )
+        {
+            if( p_sys->programs.i_size == 0 )
+            {
+                msg_Dbg( p_demux, "temporary receiving program %d", p_program->i_number );
+                p_sys->b_default_selection = true;
+                ARRAY_APPEND( p_sys->programs, p_program->i_number );
+            }
+
+            if( SetPIDFilter( p_sys, pmtpid, true ) )
+                p_sys->b_access_control = false;
+            else if ( p_sys->es_creation == DELAY_ES )
+                p_sys->es_creation = CREATE_ES;
+        }
+    }
+    p_pat->i_version = p_dvbpsipat->i_version;
+    p_pat->i_ts_id = p_dvbpsipat->i_ts_id;
+
+    for(int i=0; i<old_pmt_rm.i_size; i++)
+    {
+        /* decref current or release now unreferenced */
+        PIDRelease( p_demux, old_pmt_rm.p_elems[i] );
+    }
+    ARRAY_RESET(old_pmt_rm);
+
+    dvbpsi_pat_delete( p_dvbpsipat );
+}
+
+#define PMT_DESC_PREFIX " * PMT descriptor: "
+#define PMT_DESC_INDENT "                 : "
+static void ParsePMTRegistrations( demux_t *p_demux, const dvbpsi_descriptor_t  *p_firstdr,
+                                   ts_pmt_t *p_pmt, ts_pmt_registration_type_t *p_registration_type )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    ts_pmt_registration_type_t registration_type = *p_registration_type;
+    int i_arib_score_flags = 0; /* Descriptors can be repeated */
+
+    for( const dvbpsi_descriptor_t *p_dr = p_firstdr; p_dr != NULL; p_dr = p_dr->p_next )
+    {
+        /* general descriptors handling < 0x40 and scoring */
+        if( p_dr->i_tag < 0x40 )
+        {
+            msg_Dbg( p_demux, PMT_DESC_PREFIX "%s (0x%x)",
+                     ISO13818_1_Get_Descriptor_Description(p_dr->i_tag), p_dr->i_tag );
+        }
+
+        switch(p_dr->i_tag)
+        {
+            case 0x05: /* Registration Descriptor */
+            {
+                if( p_dr->i_length != 4 )
+                {
+                    msg_Warn( p_demux, PMT_DESC_INDENT "invalid registration descriptor" );
+                    break;
+                }
+
+                static const struct
+                {
+                    const char rgs[4];
+                    const ts_pmt_registration_type_t reg;
+                } regs[] = {
+                    { { 'H', 'D', 'M', 'V' }, TS_PMT_REGISTRATION_BLURAY },
+                    { { 'H', 'D', 'P', 'R' }, TS_PMT_REGISTRATION_BLURAY },
+                    { { 'G', 'A', '9', '4' }, TS_PMT_REGISTRATION_ATSC },
+                };
+
+                for( unsigned i=0; i<ARRAY_SIZE(regs); i++ )
+                {
+                    if( !memcmp( regs[i].rgs, p_dr->p_data, 4 ) )
+                    {
+                        registration_type = regs[i].reg;
+                        msg_Dbg( p_demux, PMT_DESC_INDENT "%4.4s registration", p_dr->p_data );
+                        break;
+                    }
+                }
+            }
+            break;
+
+            case 0x09:
+            {
+                dvbpsi_ca_dr_t *p_cadr = dvbpsi_DecodeCADr( (dvbpsi_descriptor_t *) p_dr );
+                msg_Dbg( p_demux, PMT_DESC_INDENT "CA System ID 0x%x", p_cadr->i_ca_system_id );
+                i_arib_score_flags |= (p_cadr->i_ca_system_id == 0x05);
+            }
+            break;
+
+            case 0x1d: /* We have found an IOD descriptor */
+                p_pmt->iod = IODNew( VLC_OBJECT(p_demux), p_dr->i_length, p_dr->p_data );
+                break;
+
+            case 0xC1:
+                i_arib_score_flags |= 1 << 2;
+                break;
+
+            case 0xF6:
+                i_arib_score_flags |= 1 << 1;
+                break;
+
+            default:
+                break;
+        }
+    }
+
+    if ( p_sys->arib.e_mode == ARIBMODE_AUTO &&
+         registration_type == TS_PMT_REGISTRATION_NONE &&
+         i_arib_score_flags == 0x07 ) //0b111
+    {
+        registration_type = TS_PMT_REGISTRATION_ARIB;
+        p_sys->arib.e_mode = ARIBMODE_ENABLED;
+    }
+
+    /* Now process private descriptors >= 0x40 */
+    for( const dvbpsi_descriptor_t *p_dr = p_firstdr; p_dr != NULL; p_dr = p_dr->p_next )
+    {
+        if( p_dr->i_tag < 0x40 )
+            continue;
+
+        switch(p_dr->i_tag)
+        {
+            case 0x88: /* EACEM Simulcast HD Logical channels ordering */
+                if( registration_type == TS_PMT_REGISTRATION_NONE )
+                    msg_Dbg( p_demux, PMT_DESC_PREFIX "EACEM Simulcast HD" );
+                    /* TODO: apply visibility flags */
+                break;
+
+            case 0xC1:
+                if( registration_type == TS_PMT_REGISTRATION_ARIB )
+                    msg_Dbg( p_demux, PMT_DESC_PREFIX "Digital copy control (0xC1)" );
+                break;
+
+            case 0xDE:
+                if( registration_type == TS_PMT_REGISTRATION_ARIB )
+                    msg_Dbg( p_demux, PMT_DESC_PREFIX "Content availability (0xDE)" );
+                break;
+
+            default:
+                msg_Dbg( p_demux, PMT_DESC_PREFIX "Unknown Private (0x%x)", p_dr->i_tag );
+                break;
+        }
+    }
+
+    *p_registration_type = registration_type;
+}
+
+/*****************************************************************************
+ * PMT callback and helpers
+ *****************************************************************************/
+static dvbpsi_descriptor_t *PMTEsFindDescriptor( const dvbpsi_pmt_es_t *p_es,
+                                                 int i_tag )
+{
+    dvbpsi_descriptor_t *p_dr = p_es->p_first_descriptor;;
+    while( p_dr && ( p_dr->i_tag != i_tag ) )
+        p_dr = p_dr->p_next;
+    return p_dr;
+}
+static bool PMTEsHasRegistration( demux_t *p_demux,
+                                  const dvbpsi_pmt_es_t *p_es,
+                                  const char *psz_tag )
+{
+    dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_es, 0x05 );
+    if( !p_dr )
+        return false;
+
+    if( p_dr->i_length < 4 )
+    {
+        msg_Warn( p_demux, "invalid Registration Descriptor" );
+        return false;
+    }
+
+    assert( strlen(psz_tag) == 4 );
+    return !memcmp( p_dr->p_data, psz_tag, 4 );
+}
+
+static bool PMTEsHasComponentTag( const dvbpsi_pmt_es_t *p_es,
+                                  int i_component_tag )
+{
+    dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_es, 0x52 );
+    if( !p_dr )
+        return false;
+    dvbpsi_stream_identifier_dr_t *p_si = dvbpsi_DecodeStreamIdentifierDr( p_dr );
+    if( !p_si )
+        return false;
+
+    return p_si->i_component_tag == i_component_tag;
+}
+
+static void SetupISO14496Descriptors( demux_t *p_demux, ts_pes_t *p_pes,
+                                      const ts_pmt_t *p_pmt, const dvbpsi_pmt_es_t *p_dvbpsies )
+{
+    const dvbpsi_descriptor_t *p_dr = p_dvbpsies->p_first_descriptor;
+    ts_pes_es_t *p_es = p_pes->p_es;
+
+    while( p_dr )
+    {
+        uint8_t i_length = p_dr->i_length;
+
+        switch( p_dr->i_tag )
+        {
+            case 0x1f: /* FMC Descriptor */
+                while( i_length >= 3 && !p_es->i_sl_es_id )
+                {
+                    p_es->i_sl_es_id = ( p_dr->p_data[0] << 8 ) | p_dr->p_data[1];
+                    /* FIXME: map all ids and flexmux channels */
+                    i_length -= 3;
+                    msg_Dbg( p_demux, "     - found FMC_descriptor mapping es_id=%"PRIu16, p_es->i_sl_es_id );
+                }
+                break;
+            case 0x1e: /* SL Descriptor */
+                if( i_length == 2 )
+                {
+                    p_es->i_sl_es_id = ( p_dr->p_data[0] << 8 ) | p_dr->p_data[1];
+                    msg_Dbg( p_demux, "     - found SL_descriptor mapping es_id=%"PRIu16, p_es->i_sl_es_id );
+                    ts_sections_processor_Add( &p_pes->p_sections_proc, 0x05, 0x13,
+                                               false, SLPackets_Section_Handler );
+                    p_pes->b_always_receive = true;
+                }
+                break;
+            default:
+                break;
+        }
+
+        p_dr = p_dr->p_next;
+    }
+
+    if( p_es->i_sl_es_id )
+    {
+        const es_mpeg4_descriptor_t *p_mpeg4desc = GetMPEG4DescByEsId( p_pmt, p_es->i_sl_es_id );
+        if( p_mpeg4desc && p_mpeg4desc->b_ok )
+        {
+            if( !SetupISO14496LogicalStream( p_demux, &p_mpeg4desc->dec_descr, &p_es->fmt ) )
+                msg_Dbg( p_demux, "     - IOD not yet available for es_id=%"PRIu16, p_es->i_sl_es_id );
+        }
+    }
+    else
+    {
+        switch( p_dvbpsies->i_type )
+        {
+        /* non fatal, set by packetizer */
+        case 0x0f: /* ADTS */
+        case 0x11: /* LOAS */
+            msg_Dbg( p_demux, "     - SL/FMC descriptor not found/matched" );
+            break;
+        default:
+            msg_Err( p_demux, "      - SL/FMC descriptor not found/matched" );
+            break;
+        }
+    }
+}
+
+static void SetupAVCDescriptors( demux_t *p_demux, ts_pes_es_t *p_es, const dvbpsi_pmt_es_t *p_dvbpsies )
+{
+    const dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x28 );
+    if( p_dr && p_dr->i_length >= 4 )
+    {
+        p_es->fmt.i_profile = p_dr->p_data[0];
+        p_es->fmt.i_level = p_dr->p_data[2];
+        msg_Dbg( p_demux, "     - found AVC_video_descriptor profile=0x%"PRIx8" level=0x%"PRIx8,
+                 p_es->fmt.i_profile, p_es->fmt.i_level );
+    }
+}
+
+static void SetupJ2KDescriptors( demux_t *p_demux, ts_pes_es_t *p_es, const dvbpsi_pmt_es_t *p_dvbpsies )
+{
+    const dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x32 );
+    if( p_dr && p_dr->i_length >= 24 )
+    {
+        es_format_Init( &p_es->fmt, VIDEO_ES, VLC_CODEC_JPEG2000 );
+        p_es->fmt.i_profile = p_dr->p_data[0];
+        p_es->fmt.i_level = p_dr->p_data[1];
+        p_es->fmt.video.i_width = GetDWBE(&p_dr->p_data[2]);
+        p_es->fmt.video.i_height = GetDWBE(&p_dr->p_data[6]);
+        p_es->fmt.video.i_frame_rate_base = GetWBE(&p_dr->p_data[18]);
+        p_es->fmt.video.i_frame_rate = GetWBE(&p_dr->p_data[20]);
+        p_es->b_interlaced = p_dr->p_data[23] & 0x40;
+        if( p_dr->i_length > 24 )
+        {
+            p_es->fmt.p_extra = malloc(p_dr->i_length - 24);
+            if( p_es->fmt.p_extra )
+                p_es->fmt.i_extra = p_dr->i_length - 24;
+        }
+        msg_Dbg( p_demux, "     - found J2K_video_descriptor profile=0x%"PRIx8" level=0x%"PRIx8,
+                 p_es->fmt.i_profile, p_es->fmt.i_level );
+    }
+}
+
+typedef struct
+{
+    int  i_type;
+    int  i_magazine;
+    int  i_page;
+    char p_iso639[3];
+} ts_teletext_page_t;
+
+static const char *const ppsz_teletext_type[] = {
+ "",
+ N_("Teletext"),
+ N_("Teletext subtitles"),
+ N_("Teletext: additional information"),
+ N_("Teletext: program schedule"),
+ N_("Teletext subtitles: hearing impaired")
+};
+
+static void PMTSetupEsTeletext( demux_t *p_demux, ts_pes_t *p_pes,
+                                const dvbpsi_pmt_es_t *p_dvbpsies )
+{
+    es_format_t *p_fmt = &p_pes->p_es->fmt;
+
+    ts_teletext_page_t p_page[2 * 64 + 20];
+    unsigned i_page = 0;
+
+    /* Gather pages information */
+    for( unsigned i_tag_idx = 0; i_tag_idx < 2; i_tag_idx++ )
+    {
+        dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, i_tag_idx == 0 ? 0x46 : 0x56 );
+        if( !p_dr )
+            continue;
+
+        dvbpsi_teletext_dr_t *p_sub = dvbpsi_DecodeTeletextDr( p_dr );
+        if( !p_sub )
+            continue;
+
+        for( int i = 0; i < p_sub->i_pages_number; i++ )
+        {
+            const dvbpsi_teletextpage_t *p_src = &p_sub->p_pages[i];
+
+            if( p_src->i_teletext_type >= 0x06 )
+                continue;
+
+            assert( i_page < sizeof(p_page)/sizeof(*p_page) );
+
+            ts_teletext_page_t *p_dst = &p_page[i_page++];
+
+            p_dst->i_type = p_src->i_teletext_type;
+            p_dst->i_magazine = p_src->i_teletext_magazine_number
+                ? p_src->i_teletext_magazine_number : 8;
+            p_dst->i_page = p_src->i_teletext_page_number;
+            memcpy( p_dst->p_iso639, p_src->i_iso6392_language_code, 3 );
+        }
+    }
+
+    dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x59 );
+    if( p_dr )
+    {
+        dvbpsi_subtitling_dr_t *p_sub = dvbpsi_DecodeSubtitlingDr( p_dr );
+        for( int i = 0; p_sub && i < p_sub->i_subtitles_number; i++ )
+        {
+            dvbpsi_subtitle_t *p_src = &p_sub->p_subtitle[i];
+
+            if( p_src->i_subtitling_type < 0x01 || p_src->i_subtitling_type > 0x03 )
+                continue;
+
+            assert( i_page < sizeof(p_page)/sizeof(*p_page) );
+
+            ts_teletext_page_t *p_dst = &p_page[i_page++];
+
+            switch( p_src->i_subtitling_type )
+            {
+            case 0x01:
+                p_dst->i_type = 0x02;
+                break;
+            default:
+                p_dst->i_type = 0x03;
+                break;
+            }
+            /* FIXME check if it is the right split */
+            p_dst->i_magazine = (p_src->i_composition_page_id >> 8)
+                ? (p_src->i_composition_page_id >> 8) : 8;
+            p_dst->i_page = p_src->i_composition_page_id & 0xff;
+            memcpy( p_dst->p_iso639, p_src->i_iso6392_language_code, 3 );
+        }
+    }
+
+    /* */
+    es_format_Init( p_fmt, SPU_ES, VLC_CODEC_TELETEXT );
+
+    if( !p_demux->p_sys->b_split_es || i_page <= 0 )
+    {
+        p_fmt->subs.teletext.i_magazine = -1;
+        p_fmt->subs.teletext.i_page = 0;
+        p_fmt->psz_description = strdup( vlc_gettext(ppsz_teletext_type[1]) );
+
+        dvbpsi_descriptor_t *p_dr;
+        p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x46 );
+        if( !p_dr )
+            p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x56 );
+
+        if( !p_demux->p_sys->b_split_es && p_dr && p_dr->i_length > 0 )
+        {
+            /* Descriptor pass-through */
+            p_fmt->p_extra = malloc( p_dr->i_length );
+            if( p_fmt->p_extra )
+            {
+                p_fmt->i_extra = p_dr->i_length;
+                memcpy( p_fmt->p_extra, p_dr->p_data, p_dr->i_length );
+            }
+        }
+    }
+    else
+    {
+        for( unsigned i = 0; i < i_page; i++ )
+        {
+            ts_pes_es_t *p_page_es;
+
+            /* */
+            if( i == 0 )
+            {
+                p_page_es = p_pes->p_es;
+            }
+            else
+            {
+                p_page_es = ts_pes_es_New( p_pes->p_es->p_program );
+                if( !p_page_es )
+                    break;
+
+                es_format_Copy( &p_page_es->fmt, p_fmt );
+                free( p_page_es->fmt.psz_language );
+                free( p_page_es->fmt.psz_description );
+                p_page_es->fmt.psz_language = NULL;
+                p_page_es->fmt.psz_description = NULL;
+                ts_pes_Add_es( p_pes, p_page_es, true );
+            }
+
+            /* */
+            const ts_teletext_page_t *p = &p_page[i];
+            p_page_es->fmt.i_priority = (p->i_type == 0x02 || p->i_type == 0x05) ?
+                      ES_PRIORITY_SELECTABLE_MIN : ES_PRIORITY_NOT_DEFAULTABLE;
+            p_page_es->fmt.psz_language = strndup( p->p_iso639, 3 );
+            p_page_es->fmt.psz_description = strdup(vlc_gettext(ppsz_teletext_type[p->i_type]));
+            p_page_es->fmt.subs.teletext.i_magazine = p->i_magazine;
+            p_page_es->fmt.subs.teletext.i_page = p->i_page;
+
+            msg_Dbg( p_demux,
+                         "    * ttxt type=%s lan=%s page=%d%02x",
+                         p_page_es->fmt.psz_description,
+                         p_page_es->fmt.psz_language,
+                         p->i_magazine, p->i_page );
+        }
+    }
+}
+static void PMTSetupEsDvbSubtitle( demux_t *p_demux, ts_pes_t *p_pes,
+                                   const dvbpsi_pmt_es_t *p_dvbpsies )
+{
+    es_format_t *p_fmt = &p_pes->p_es->fmt;
+
+    es_format_Init( p_fmt, SPU_ES, VLC_CODEC_DVBS );
+
+    dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x59 );
+    int i_page = 0;
+    dvbpsi_subtitling_dr_t *p_sub = dvbpsi_DecodeSubtitlingDr( p_dr );
+    for( int i = 0; p_sub && i < p_sub->i_subtitles_number; i++ )
+    {
+        const int i_type = p_sub->p_subtitle[i].i_subtitling_type;
+        if( ( i_type >= 0x10 && i_type <= 0x14 ) ||
+            ( i_type >= 0x20 && i_type <= 0x24 ) )
+            i_page++;
+    }
+
+    if( !p_demux->p_sys->b_split_es  || i_page <= 0 )
+    {
+        p_fmt->subs.dvb.i_id = -1;
+        p_fmt->psz_description = strdup( _("DVB subtitles") );
+
+        if( !p_demux->p_sys->b_split_es && p_dr && p_dr->i_length > 0 )
+        {
+            /* Descriptor pass-through */
+            p_fmt->p_extra = malloc( p_dr->i_length );
+            if( p_fmt->p_extra )
+            {
+                p_fmt->i_extra = p_dr->i_length;
+                memcpy( p_fmt->p_extra, p_dr->p_data, p_dr->i_length );
+            }
+        }
+    }
+    else
+    {
+        for( int i = 0; i < p_sub->i_subtitles_number; i++ )
+        {
+            ts_pes_es_t *p_subs_es;
+
+            /* */
+            if( i == 0 )
+            {
+                p_subs_es = p_pes->p_es;
+            }
+            else
+            {
+                p_subs_es = ts_pes_es_New( p_pes->p_es->p_program );
+                if( !p_subs_es )
+                    break;
+
+                es_format_Copy( &p_subs_es->fmt, p_fmt );
+                free( p_subs_es->fmt.psz_language );
+                free( p_subs_es->fmt.psz_description );
+                p_subs_es->fmt.psz_language = NULL;
+                p_subs_es->fmt.psz_description = NULL;
+
+                ts_pes_Add_es( p_pes, p_subs_es, true );
+            }
+
+            /* */
+            const dvbpsi_subtitle_t *p = &p_sub->p_subtitle[i];
+            p_subs_es->fmt.psz_language = strndup( (char *)p->i_iso6392_language_code, 3 );
+            switch( p->i_subtitling_type )
+            {
+            case 0x10: /* unspec. */
+            case 0x11: /* 4:3 */
+            case 0x12: /* 16:9 */
+            case 0x13: /* 2.21:1 */
+            case 0x14: /* HD monitor */
+                p_subs_es->fmt.psz_description = strdup( _("DVB subtitles") );
+                break;
+            case 0x20: /* Hearing impaired unspec. */
+            case 0x21: /* h.i. 4:3 */
+            case 0x22: /* h.i. 16:9 */
+            case 0x23: /* h.i. 2.21:1 */
+            case 0x24: /* h.i. HD monitor */
+                p_subs_es->fmt.psz_description = strdup( _("DVB subtitles: hearing impaired") );
+                break;
+            default:
+                break;
+            }
+
+            /* Hack, FIXME */
+            p_subs_es->fmt.subs.dvb.i_id = ( p->i_composition_page_id <<  0 ) |
+                                      ( p->i_ancillary_page_id   << 16 );
+        }
+    }
+}
+
+static int vlc_ceil_log2( const unsigned int val )
+{
+    int n = 31 - clz(val);
+    if ((1U << n) != val)
+        n++;
+
+    return n;
+}
+
+static void OpusSetup(demux_t *demux, uint8_t *p, size_t len, es_format_t *p_fmt)
+{
+    OpusHeader h;
+
+    /* default mapping */
+    static const unsigned char map[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+    memcpy(h.stream_map, map, sizeof(map));
+
+    int csc, mapping;
+    int channels = 0;
+    int stream_count = 0;
+    int ccc = p[1]; // channel_config_code
+    if (ccc <= 8) {
+        channels = ccc;
+        if (channels)
+            mapping = channels > 2;
+        else {
+            mapping = 255;
+            channels = 2; // dual mono
+        }
+        static const uint8_t p_csc[8] = { 0, 1, 1, 2, 2, 2, 3, 3 };
+        csc = p_csc[channels - 1];
+        stream_count = channels - csc;
+
+        static const uint8_t map[6][7] = {
+            { 2,1 },
+            { 1,2,3 },
+            { 4,1,2,3 },
+            { 4,1,2,3,5 },
+            { 4,1,2,3,5,6 },
+            { 6,1,2,3,4,5,7 },
+        };
+        if (channels > 2)
+            memcpy(&h.stream_map[1], map[channels-3], channels - 1);
+    } else if (ccc == 0x81) {
+        if (len < 4)
+            goto explicit_config_too_short;
+
+        channels = p[2];
+        mapping = p[3];
+        csc = 0;
+        if (mapping) {
+            bs_t s;
+            bs_init(&s, &p[4], len - 4);
+            stream_count = 1;
+            if (channels) {
+                int bits = vlc_ceil_log2(channels);
+                if (s.i_left < bits)
+                    goto explicit_config_too_short;
+                stream_count = bs_read(&s, bits) + 1;
+                bits = vlc_ceil_log2(stream_count + 1);
+                if (s.i_left < bits)
+                    goto explicit_config_too_short;
+                csc = bs_read(&s, bits);
+            }
+            int channel_bits = vlc_ceil_log2(stream_count + csc + 1);
+            if (s.i_left < channels * channel_bits)
+                goto explicit_config_too_short;
+
+            unsigned char silence = (1U << (stream_count + csc + 1)) - 1;
+            for (int i = 0; i < channels; i++) {
+                unsigned char m = bs_read(&s, channel_bits);
+                if (m == silence)
+                    m = 0xff;
+                h.stream_map[i] = m;
+            }
+        }
+    } else if (ccc >= 0x80 && ccc <= 0x88) {
+        channels = ccc - 0x80;
+        if (channels)
+            mapping = 1;
+        else {
+            mapping = 255;
+            channels = 2; // dual mono
+        }
+        csc = 0;
+        stream_count = channels;
+    } else {
+        msg_Err(demux, "Opus channel configuration 0x%.2x is reserved", ccc);
+    }
+
+    if (!channels) {
+        msg_Err(demux, "Opus channel configuration 0x%.2x not supported yet", p[1]);
+        return;
+    }
+
+    opus_prepare_header(channels, 0, &h);
+    h.preskip = 0;
+    h.input_sample_rate = 48000;
+    h.nb_coupled = csc;
+    h.nb_streams = channels - csc;
+    h.channel_mapping = mapping;
+
+    if (h.channels) {
+        opus_write_header((uint8_t**)&p_fmt->p_extra, &p_fmt->i_extra, &h, NULL /* FIXME */);
+        if (p_fmt->p_extra) {
+            p_fmt->i_cat = AUDIO_ES;
+            p_fmt->i_codec = VLC_CODEC_OPUS;
+            p_fmt->audio.i_channels = h.channels;
+            p_fmt->audio.i_rate = 48000;
+        }
+    }
+
+    return;
+
+explicit_config_too_short:
+    msg_Err(demux, "Opus descriptor too short");
+}
+
+static void PMTSetupEs0x06( demux_t *p_demux, ts_pes_t *p_pes,
+                            const dvbpsi_pmt_es_t *p_dvbpsies )
+{
+    es_format_t *p_fmt = &p_pes->p_es->fmt;
+    dvbpsi_descriptor_t *p_subs_dr = PMTEsFindDescriptor( p_dvbpsies, 0x59 );
+    dvbpsi_descriptor_t *desc;
+    if( PMTEsHasRegistration( p_demux, p_dvbpsies, "EAC3" ) ||
+        PMTEsFindDescriptor( p_dvbpsies, 0x7a ) )
+    {
+        /* DVB with stream_type 0x06 (ETS EN 300 468) */
+        p_fmt->i_cat = AUDIO_ES;
+        p_fmt->i_codec = VLC_CODEC_EAC3;
+    }
+    else if( PMTEsHasRegistration( p_demux, p_dvbpsies, "AC-3" ) ||
+             PMTEsFindDescriptor( p_dvbpsies, 0x6a ) ||
+             PMTEsFindDescriptor( p_dvbpsies, 0x81 ) ) /* AC-3 channel (also in EAC3) */
+    {
+        p_fmt->i_cat = AUDIO_ES;
+        p_fmt->i_codec = VLC_CODEC_A52;
+    }
+    else if( (desc = PMTEsFindDescriptor( p_dvbpsies, 0x7f ) ) && desc->i_length >= 2 &&
+              PMTEsHasRegistration(p_demux, p_dvbpsies, "Opus"))
+    {
+        OpusSetup(p_demux, desc->p_data, desc->i_length, p_fmt);
+    }
+    else if( PMTEsHasRegistration( p_demux, p_dvbpsies, "DTS1" ) ||
+             PMTEsHasRegistration( p_demux, p_dvbpsies, "DTS2" ) ||
+             PMTEsHasRegistration( p_demux, p_dvbpsies, "DTS3" ) ||
+             PMTEsFindDescriptor( p_dvbpsies, 0x73 ) )
+    {
+        /*registration descriptor(ETSI TS 101 154 Annex F)*/
+        p_fmt->i_cat = AUDIO_ES;
+        p_fmt->i_codec = VLC_CODEC_DTS;
+    }
+    else if( PMTEsHasRegistration( p_demux, p_dvbpsies, "BSSD" ) && !p_subs_dr )
+    {
+        /* BSSD is AES3 DATA, but could also be subtitles
+         * we need to check for secondary descriptor then s*/
+        p_fmt->i_cat = AUDIO_ES;
+        p_fmt->b_packetized = true;
+        p_fmt->i_codec = VLC_CODEC_302M;
+    }
+    else if( PMTEsHasRegistration( p_demux, p_dvbpsies, "HEVC" ) )
+    {
+        p_fmt->i_cat = VIDEO_ES;
+        p_fmt->i_codec = VLC_CODEC_HEVC;
+    }
+    else if ( p_demux->p_sys->arib.e_mode == ARIBMODE_ENABLED )
+    {
+        /* Lookup our data component descriptor first ARIB STD B10 6.4 */
+        dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0xFD );
+        /* and check that it maps to something ARIB STD B14 Table 5.1/5.2 */
+        if ( p_dr && p_dr->i_length >= 2 )
+        {
+            if( !memcmp( p_dr->p_data, "\x00\x08", 2 ) &&  (
+                    PMTEsHasComponentTag( p_dvbpsies, 0x30 ) ||
+                    PMTEsHasComponentTag( p_dvbpsies, 0x31 ) ||
+                    PMTEsHasComponentTag( p_dvbpsies, 0x32 ) ||
+                    PMTEsHasComponentTag( p_dvbpsies, 0x33 ) ||
+                    PMTEsHasComponentTag( p_dvbpsies, 0x34 ) ||
+                    PMTEsHasComponentTag( p_dvbpsies, 0x35 ) ||
+                    PMTEsHasComponentTag( p_dvbpsies, 0x36 ) ||
+                    PMTEsHasComponentTag( p_dvbpsies, 0x37 ) ) )
+            {
+                es_format_Init( p_fmt, SPU_ES, VLC_CODEC_ARIB_A );
+                p_fmt->psz_language = strndup ( "jpn", 3 );
+                p_fmt->psz_description = strdup( _("ARIB subtitles") );
+            }
+            else if( !memcmp( p_dr->p_data, "\x00\x12", 2 ) && (
+                     PMTEsHasComponentTag( p_dvbpsies, 0x87 ) ||
+                     PMTEsHasComponentTag( p_dvbpsies, 0x88 ) ) )
+            {
+                es_format_Init( p_fmt, SPU_ES, VLC_CODEC_ARIB_C );
+                p_fmt->psz_language = strndup ( "jpn", 3 );
+                p_fmt->psz_description = strdup( _("ARIB subtitles") );
+            }
+        }
+    }
+    else
+    {
+        /* Subtitle/Teletext/VBI fallbacks */
+        dvbpsi_subtitling_dr_t *p_sub;
+        if( p_subs_dr && ( p_sub = dvbpsi_DecodeSubtitlingDr( p_subs_dr ) ) )
+        {
+            for( int i = 0; i < p_sub->i_subtitles_number; i++ )
+            {
+                if( p_fmt->i_cat != UNKNOWN_ES )
+                    break;
+
+                switch( p_sub->p_subtitle[i].i_subtitling_type )
+                {
+                case 0x01: /* EBU Teletext subtitles */
+                case 0x02: /* Associated EBU Teletext */
+                case 0x03: /* VBI data */
+                    PMTSetupEsTeletext( p_demux, p_pes, p_dvbpsies );
+                    break;
+                case 0x10: /* DVB Subtitle (normal) with no monitor AR critical */
+                case 0x11: /*                 ...   on 4:3 AR monitor */
+                case 0x12: /*                 ...   on 16:9 AR monitor */
+                case 0x13: /*                 ...   on 2.21:1 AR monitor */
+                case 0x14: /*                 ...   for display on a high definition monitor */
+                case 0x20: /* DVB Subtitle (impaired) with no monitor AR critical */
+                case 0x21: /*                 ...   on 4:3 AR monitor */
+                case 0x22: /*                 ...   on 16:9 AR monitor */
+                case 0x23: /*                 ...   on 2.21:1 AR monitor */
+                case 0x24: /*                 ...   for display on a high definition monitor */
+                    PMTSetupEsDvbSubtitle( p_demux, p_pes, p_dvbpsies );
+                    break;
+                default:
+                    msg_Err( p_demux, "Unrecognized DVB subtitle type (0x%x)",
+                             p_sub->p_subtitle[i].i_subtitling_type );
+                    break;
+                }
+            }
+        }
+
+        if( p_fmt->i_cat == UNKNOWN_ES &&
+            ( PMTEsFindDescriptor( p_dvbpsies, 0x45 ) ||  /* VBI Data descriptor */
+              PMTEsFindDescriptor( p_dvbpsies, 0x46 ) ||  /* VBI Teletext descriptor */
+              PMTEsFindDescriptor( p_dvbpsies, 0x56 ) ) ) /* EBU Teletext descriptor */
+        {
+            /* Teletext/VBI */
+            PMTSetupEsTeletext( p_demux, p_pes, p_dvbpsies );
+        }
+    }
+
+    /* FIXME is it useful ? */
+    if( PMTEsFindDescriptor( p_dvbpsies, 0x52 ) )
+    {
+        dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x52 );
+        dvbpsi_stream_identifier_dr_t *p_si = dvbpsi_DecodeStreamIdentifierDr( p_dr );
+
+        msg_Dbg( p_demux, "    * Stream Component Identifier: %d", p_si->i_component_tag );
+    }
+}
+
+static void PMTSetupEs0xEA( demux_t *p_demux, ts_pes_es_t *p_es,
+                           const dvbpsi_pmt_es_t *p_dvbpsies )
+{
+    /* Registration Descriptor */
+    if( !PMTEsHasRegistration( p_demux, p_dvbpsies, "VC-1" ) )
+    {
+        msg_Err( p_demux, "Registration descriptor not found or invalid" );
+        return;
+    }
+
+    es_format_t *p_fmt = &p_es->fmt;
+
+    /* registration descriptor for VC-1 (SMPTE rp227) */
+    p_fmt->i_cat = VIDEO_ES;
+    p_fmt->i_codec = VLC_CODEC_VC1;
+
+    /* XXX With Simple and Main profile the SEQUENCE
+     * header is modified: video width and height are
+     * inserted just after the start code as 2 int16_t
+     * The packetizer will take care of that. */
+}
+
+static void PMTSetupEs0xD1( demux_t *p_demux, ts_pes_es_t *p_es,
+                           const dvbpsi_pmt_es_t *p_dvbpsies )
+{
+    /* Registration Descriptor */
+    if( !PMTEsHasRegistration( p_demux, p_dvbpsies, "drac" ) )
+    {
+        msg_Err( p_demux, "Registration descriptor not found or invalid" );
+        return;
+    }
+
+    es_format_t *p_fmt = &p_es->fmt;
+
+    /* registration descriptor for Dirac
+     * (backwards compatable with VC-2 (SMPTE Sxxxx:2008)) */
+    p_fmt->i_cat = VIDEO_ES;
+    p_fmt->i_codec = VLC_CODEC_DIRAC;
+}
+
+static void PMTSetupEs0xA0( demux_t *p_demux, ts_pes_es_t *p_es,
+                           const dvbpsi_pmt_es_t *p_dvbpsies )
+{
+    /* MSCODEC sent by vlc */
+    dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0xa0 );
+    if( !p_dr || p_dr->i_length < 10 )
+    {
+        msg_Warn( p_demux,
+                  "private MSCODEC (vlc) without bih private descriptor" );
+        return;
+    }
+
+    es_format_t *p_fmt = &p_es->fmt;
+    p_fmt->i_cat = VIDEO_ES;
+    p_fmt->i_codec = VLC_FOURCC( p_dr->p_data[0], p_dr->p_data[1],
+                                 p_dr->p_data[2], p_dr->p_data[3] );
+    p_fmt->video.i_width = GetWBE( &p_dr->p_data[4] );
+    p_fmt->video.i_height = GetWBE( &p_dr->p_data[6] );
+    p_fmt->video.i_visible_width = p_fmt->video.i_width;
+    p_fmt->video.i_visible_height = p_fmt->video.i_height;
+    p_fmt->i_extra = GetWBE( &p_dr->p_data[8] );
+
+    if( p_fmt->i_extra > 0 )
+    {
+        p_fmt->p_extra = malloc( p_fmt->i_extra );
+        if( p_fmt->p_extra )
+            memcpy( p_fmt->p_extra, &p_dr->p_data[10],
+                    __MIN( p_fmt->i_extra, p_dr->i_length - 10 ) );
+        else
+            p_fmt->i_extra = 0;
+    }
+    /* For such stream we will gather them ourself and don't launch a
+     * packetizer.
+     * Yes it's ugly but it's the only way to have DIV3 working */
+    p_fmt->b_packetized = true;
+}
+
+static void PMTSetupEs0x83( const dvbpsi_pmt_t *p_pmt, ts_pes_es_t *p_es, int i_pid )
+{
+    /* WiDi broadcasts without registration on PMT 0x1, PCR 0x1000 and
+     * with audio track pid being 0x1100..0x11FF */
+    if ( p_pmt->i_program_number == 0x1 &&
+         p_pmt->i_pcr_pid == 0x1000 &&
+        ( i_pid >> 8 ) == 0x11 )
+    {
+        /* Not enough ? might contain 0x83 private descriptor, 2 bytes 0x473F */
+        es_format_Init( &p_es->fmt, AUDIO_ES, VLC_CODEC_WIDI_LPCM );
+    }
+    else
+        es_format_Init( &p_es->fmt, AUDIO_ES, VLC_CODEC_DVD_LPCM );
+}
+
+static bool PMTSetupEsHDMV( demux_t *p_demux, ts_pes_es_t *p_es,
+                            const dvbpsi_pmt_es_t *p_dvbpsies )
+{
+    es_format_t *p_fmt = &p_es->fmt;
+
+    /* Blu-Ray mapping */
+    switch( p_dvbpsies->i_type )
+    {
+    case 0x80:
+        p_fmt->i_cat = AUDIO_ES;
+        p_fmt->i_codec = VLC_CODEC_BD_LPCM;
+        break;
+    case 0x81:
+        p_fmt->i_cat = AUDIO_ES;
+        p_fmt->i_codec = VLC_CODEC_A52;
+        break;
+    case 0x82:
+    case 0x85: /* DTS-HD High resolution audio */
+    case 0x86: /* DTS-HD Master audio */
+    case 0xA2: /* Secondary DTS audio */
+        p_fmt->i_cat = AUDIO_ES;
+        p_fmt->i_codec = VLC_CODEC_DTS;
+        break;
+
+    case 0x83: /* TrueHD AC3 */
+        p_fmt->i_cat = AUDIO_ES;
+        p_fmt->i_codec = VLC_CODEC_TRUEHD;
+        break;
+
+    case 0x84: /* E-AC3 */
+    case 0xA1: /* Secondary E-AC3 */
+        p_fmt->i_cat = AUDIO_ES;
+        p_fmt->i_codec = VLC_CODEC_EAC3;
+        break;
+    case 0x90: /* Presentation graphics */
+        p_fmt->i_cat = SPU_ES;
+        p_fmt->i_codec = VLC_CODEC_BD_PG;
+        break;
+    case 0x91: /* Interactive graphics */
+    case 0x92: /* Subtitle */
+        return false;
+    case 0xEA:
+        p_fmt->i_cat = VIDEO_ES;
+        p_fmt->i_codec = VLC_CODEC_VC1;
+        break;
+    default:
+        msg_Info( p_demux, "HDMV registration not implemented for pid 0x%x type 0x%x",
+                  p_dvbpsies->i_pid, p_dvbpsies->i_type );
+        return false;
+        break;
+    }
+    return true;
+}
+
+static bool PMTSetupEsRegistration( demux_t *p_demux, ts_pes_es_t *p_es,
+                                    const dvbpsi_pmt_es_t *p_dvbpsies )
+{
+    static const struct
+    {
+        char         psz_tag[5];
+        int          i_cat;
+        vlc_fourcc_t i_codec;
+    } p_regs[] = {
+        { "AC-3", AUDIO_ES, VLC_CODEC_A52   },
+        { "DTS1", AUDIO_ES, VLC_CODEC_DTS   },
+        { "DTS2", AUDIO_ES, VLC_CODEC_DTS   },
+        { "DTS3", AUDIO_ES, VLC_CODEC_DTS   },
+        { "BSSD", AUDIO_ES, VLC_CODEC_302M  },
+        { "VC-1", VIDEO_ES, VLC_CODEC_VC1   },
+        { "drac", VIDEO_ES, VLC_CODEC_DIRAC },
+        { "", UNKNOWN_ES, 0 }
+    };
+    es_format_t *p_fmt = &p_es->fmt;
+
+    for( int i = 0; p_regs[i].i_cat != UNKNOWN_ES; i++ )
+    {
+        if( PMTEsHasRegistration( p_demux, p_dvbpsies, p_regs[i].psz_tag ) )
+        {
+            p_fmt->i_cat   = p_regs[i].i_cat;
+            p_fmt->i_codec = p_regs[i].i_codec;
+            if (p_dvbpsies->i_type == 0x87)
+                p_fmt->i_codec = VLC_CODEC_EAC3;
+            return true;
+        }
+    }
+    return false;
+}
+
+static char *GetAudioTypeDesc(demux_t *p_demux, int type)
+{
+    static const char *audio_type[] = {
+        NULL,
+        N_("clean effects"),
+        N_("hearing impaired"),
+        N_("visual impaired commentary"),
+    };
+
+    if (type < 0 || type > 3)
+        msg_Dbg( p_demux, "unknown audio type: %d", type);
+    else if (type > 0)
+        return strdup(audio_type[type]);
+
+    return NULL;
+}
+static void PMTParseEsIso639( demux_t *p_demux, ts_pes_es_t *p_es,
+                              const dvbpsi_pmt_es_t *p_dvbpsies )
+{
+    /* get language descriptor */
+    dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x0a );
+
+    if( !p_dr )
+        return;
+
+    dvbpsi_iso639_dr_t *p_decoded = dvbpsi_DecodeISO639Dr( p_dr );
+    if( !p_decoded )
+    {
+        msg_Err( p_demux, "      Failed to decode a ISO 639 descriptor" );
+        return;
+    }
+
+#if defined(DR_0A_API_VER) && (DR_0A_API_VER >= 2)
+    p_es->fmt.psz_language = malloc( 4 );
+    if( p_es->fmt.psz_language )
+    {
+        memcpy( p_es->fmt.psz_language, p_decoded->code[0].iso_639_code, 3 );
+        p_es->fmt.psz_language[3] = 0;
+        msg_Dbg( p_demux, "      found language: %s", p_es->fmt.psz_language);
+    }
+    int type = p_decoded->code[0].i_audio_type;
+    p_es->fmt.psz_description = GetAudioTypeDesc(p_demux, type);
+    if (type == 0)
+        p_es->fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN + 1; // prioritize normal audio tracks
+
+    p_es->fmt.i_extra_languages = p_decoded->i_code_count-1;
+    if( p_es->fmt.i_extra_languages > 0 )
+        p_es->fmt.p_extra_languages =
+            malloc( sizeof(*p_es->fmt.p_extra_languages) *
+                    p_es->fmt.i_extra_languages );
+    if( p_es->fmt.p_extra_languages )
+    {
+        for( unsigned i = 0; i < p_es->fmt.i_extra_languages; i++ )
+        {
+            p_es->fmt.p_extra_languages[i].psz_language = malloc(4);
+            if( p_es->fmt.p_extra_languages[i].psz_language )
+            {
+                memcpy( p_es->fmt.p_extra_languages[i].psz_language,
+                    p_decoded->code[i+1].iso_639_code, 3 );
+                p_es->fmt.p_extra_languages[i].psz_language[3] = '\0';
+            }
+            int type = p_decoded->code[i].i_audio_type;
+            p_es->fmt.p_extra_languages[i].psz_description = GetAudioTypeDesc(p_demux, type);
+        }
+    }
+#else
+    p_es->fmt.psz_language = malloc( 4 );
+    if( p_es->fmt.psz_language )
+    {
+        memcpy( p_es->fmt.psz_language,
+                p_decoded->i_iso_639_code, 3 );
+        p_es->fmt.psz_language[3] = 0;
+    }
+#endif
+}
+
+static void PIDFillFormat( ts_pes_t *p_pes, int i_stream_type, ts_es_data_type_t *p_datatype )
+{
+    es_format_t *fmt = &p_pes->p_es->fmt;
+    switch( i_stream_type )
+    {
+    case 0x01:  /* MPEG-1 video */
+    case 0x02:  /* MPEG-2 video */
+    case 0x80:  /* MPEG-2 MOTO video */
+        es_format_Init( fmt, VIDEO_ES, VLC_CODEC_MPGV );
+        break;
+    case 0x03:  /* MPEG-1 audio */
+    case 0x04:  /* MPEG-2 audio */
+        es_format_Init( fmt, AUDIO_ES, VLC_CODEC_MPGA );
+        break;
+    case 0x11:  /* MPEG4 (audio) LATM */
+    case 0x0f:  /* ISO/IEC 13818-7 Audio with ADTS transport syntax */
+    case 0x1c:  /* ISO/IEC 14496-3 Audio, without using any additional
+                   transport syntax, such as DST, ALS and SLS */
+        es_format_Init( fmt, AUDIO_ES, VLC_CODEC_MP4A );
+        break;
+    case 0x10:  /* MPEG4 (video) */
+        es_format_Init( fmt, VIDEO_ES, VLC_CODEC_MP4V );
+        break;
+    case 0x1B:  /* H264 <- check transport syntax/needed descriptor */
+        es_format_Init( fmt, VIDEO_ES, VLC_CODEC_H264 );
+        break;
+    case 0x24:  /* HEVC */
+        es_format_Init( fmt, VIDEO_ES, VLC_CODEC_HEVC );
+        break;
+    case 0x42:  /* CAVS (Chinese AVS) */
+        es_format_Init( fmt, VIDEO_ES, VLC_CODEC_CAVS );
+        break;
+
+    case 0x81:  /* A52 (audio) */
+        es_format_Init( fmt, AUDIO_ES, VLC_CODEC_A52 );
+        break;
+    case 0x82:  /* SCTE-27 (sub) */
+        es_format_Init( fmt, SPU_ES, VLC_CODEC_SCTE_27 );
+        *p_datatype = TS_ES_DATA_TABLE_SECTION;
+        ts_sections_processor_Add( &p_pes->p_sections_proc, 0xC6, 0x82,
+                                   true, SCTE27_Section_Handler );
+        break;
+    case 0x84:  /* SDDS (audio) */
+        es_format_Init( fmt, AUDIO_ES, VLC_CODEC_SDDS );
+        break;
+    case 0x85:  /* DTS (audio) */
+        es_format_Init( fmt, AUDIO_ES, VLC_CODEC_DTS );
+        break;
+    case 0x87: /* E-AC3 */
+        es_format_Init( fmt, AUDIO_ES, VLC_CODEC_EAC3 );
+        break;
+
+    case 0x91:  /* A52 vls (audio) */
+        es_format_Init( fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', 'b' ) );
+        break;
+    case 0x92:  /* DVD_SPU vls (sub) */
+        es_format_Init( fmt, SPU_ES, VLC_FOURCC( 's', 'p', 'u', 'b' ) );
+        break;
+
+    case 0x94:  /* SDDS (audio) */
+        es_format_Init( fmt, AUDIO_ES, VLC_FOURCC( 's', 'd', 'd', 'b' ) );
+        break;
+
+    case 0xa0:  /* MSCODEC vlc (video) (fixed later) */
+        es_format_Init( fmt, UNKNOWN_ES, 0 );
+        break;
+
+    case 0x06:  /* PES_PRIVATE  (fixed later) */
+    case 0x12:  /* MPEG-4 generic (sub/scene/...) (fixed later) */
+    case 0xEA:  /* Privately managed ES (VC-1) (fixed later */
+    default:
+        es_format_Init( fmt, UNKNOWN_ES, 0 );
+        break;
+    }
+
+    /* PES packets usually contain truncated frames */
+    fmt->b_packetized = false;
+}
+
+static void FillPESFromDvbpsiES( demux_t *p_demux,
+                                 const dvbpsi_pmt_t *p_dvbpsipmt,
+                                 const dvbpsi_pmt_es_t *p_dvbpsies,
+                                 ts_pmt_registration_type_t registration_type,
+                                 const ts_pmt_t *p_pmt,
+                                 ts_pes_t *p_pes )
+{
+    ts_es_data_type_t type_change = TS_ES_DATA_PES;
+    PIDFillFormat( p_pes, p_dvbpsies->i_type, &type_change );
+
+    p_pes->i_stream_type = p_dvbpsies->i_type;
+
+    bool b_registration_applied = false;
+    if ( p_dvbpsies->i_type >= 0x80 ) /* non standard, extensions */
+    {
+        if ( registration_type == TS_PMT_REGISTRATION_BLURAY )
+        {
+            if (( b_registration_applied = PMTSetupEsHDMV( p_demux, p_pes->p_es, p_dvbpsies ) ))
+                msg_Dbg( p_demux, "    + HDMV registration applied to pid %d type 0x%x",
+                         p_dvbpsies->i_pid, p_dvbpsies->i_type );
+        }
+        else
+        {
+            if (( b_registration_applied = PMTSetupEsRegistration( p_demux, p_pes->p_es, p_dvbpsies ) ))
+                msg_Dbg( p_demux, "    + registration applied to pid %d type 0x%x",
+                    p_dvbpsies->i_pid, p_dvbpsies->i_type );
+        }
+    }
+
+    if ( !b_registration_applied )
+    {
+        p_pes->data_type = type_change; /* Only change type if registration has not changed meaning */
+
+        switch( p_dvbpsies->i_type )
+        {
+        case 0x06:
+            /* Handle PES private data */
+            PMTSetupEs0x06( p_demux, p_pes, p_dvbpsies );
+            break;
+        /* All other private or reserved types */
+        case 0x13: /* SL in sections */
+            p_pes->data_type = TS_ES_DATA_TABLE_SECTION;
+            //ft
+        case 0x0f:
+        case 0x10:
+        case 0x11:
+        case 0x12:
+            SetupISO14496Descriptors( p_demux, p_pes, p_pmt, p_dvbpsies );
+            break;
+        case 0x1b:
+            SetupAVCDescriptors( p_demux, p_pes->p_es, p_dvbpsies );
+            break;
+        case 0x21:
+            SetupJ2KDescriptors( p_demux, p_pes->p_es, p_dvbpsies );
+            break;
+        case 0x83:
+            /* LPCM (audio) */
+            PMTSetupEs0x83( p_dvbpsipmt, p_pes->p_es, p_dvbpsies->i_pid );
+            break;
+        case 0xa0:
+            PMTSetupEs0xA0( p_demux, p_pes->p_es, p_dvbpsies );
+            break;
+        case 0xd1:
+            PMTSetupEs0xD1( p_demux, p_pes->p_es, p_dvbpsies );
+            break;
+        case 0xEA:
+            PMTSetupEs0xEA( p_demux, p_pes->p_es, p_dvbpsies );
+        default:
+            break;
+        }
+    }
+
+    if( p_pes->p_es->fmt.i_cat == AUDIO_ES ||
+        ( p_pes->p_es->fmt.i_cat == SPU_ES &&
+          p_pes->p_es->fmt.i_codec != VLC_CODEC_DVBS &&
+          p_pes->p_es->fmt.i_codec != VLC_CODEC_TELETEXT ) )
+    {
+        PMTParseEsIso639( p_demux, p_pes->p_es, p_dvbpsies );
+    }
+
+    /* Set Groups / ID */
+    p_pes->p_es->fmt.i_group = p_dvbpsipmt->i_program_number;
+    if( p_demux->p_sys->b_es_id_pid )
+        p_pes->p_es->fmt.i_id = p_dvbpsies->i_pid;
+}
+
+void PMTCallBack( void *data, dvbpsi_pmt_t *p_dvbpsipmt )
+{
+    demux_t      *p_demux = data;
+    demux_sys_t  *p_sys = p_demux->p_sys;
+
+    ts_pid_t     *pmtpid = NULL;
+    ts_pmt_t     *p_pmt = NULL;
+
+    msg_Dbg( p_demux, "PMTCallBack called for program %d", p_dvbpsipmt->i_program_number );
+
+    if (unlikely(GetPID(p_sys, 0)->type != TYPE_PAT))
+    {
+        assert(GetPID(p_sys, 0)->type == TYPE_PAT);
+        dvbpsi_pmt_delete(p_dvbpsipmt);
+    }
+
+    const ts_pat_t *p_pat = GetPID(p_sys, 0)->u.p_pat;
+
+    /* First find this PMT declared in PAT */
+    for( int i = 0; !pmtpid && i < p_pat->programs.i_size; i++ )
+    {
+        const int i_pmt_prgnumber = p_pat->programs.p_elems[i]->u.p_pmt->i_number;
+        if( i_pmt_prgnumber != TS_USER_PMT_NUMBER &&
+            i_pmt_prgnumber == p_dvbpsipmt->i_program_number )
+        {
+            pmtpid = p_pat->programs.p_elems[i];
+            assert(pmtpid->type == TYPE_PMT);
+            p_pmt = pmtpid->u.p_pmt;
+        }
+    }
+
+    if( pmtpid == NULL )
+    {
+        msg_Warn( p_demux, "unreferenced program (broken stream)" );
+        dvbpsi_pmt_delete(p_dvbpsipmt);
+        return;
+    }
+
+    pmtpid->i_flags |= FLAG_SEEN;
+
+    if( p_pmt->i_version != -1 &&
+        ( !p_dvbpsipmt->b_current_next || p_pmt->i_version == p_dvbpsipmt->i_version ) )
+    {
+        dvbpsi_pmt_delete( p_dvbpsipmt );
+        return;
+    }
+
+    /* Save old es array */
+    DECL_ARRAY(ts_pid_t *) pid_to_decref;
+    pid_to_decref.i_alloc = p_pmt->e_streams.i_alloc;
+    pid_to_decref.i_size = p_pmt->e_streams.i_size;
+    pid_to_decref.p_elems = p_pmt->e_streams.p_elems;
+    if( p_pmt->p_mgt )
+        ARRAY_APPEND( pid_to_decref, p_pmt->p_mgt );
+    ARRAY_INIT(p_pmt->e_streams);
+
+    if( p_pmt->iod )
+    {
+        ODFree( p_pmt->iod );
+        p_pmt->iod = NULL;
+    }
+
+    msg_Dbg( p_demux, "new PMT program number=%d version=%d pid_pcr=%d",
+             p_dvbpsipmt->i_program_number, p_dvbpsipmt->i_version, p_dvbpsipmt->i_pcr_pid );
+    p_pmt->i_pid_pcr = p_dvbpsipmt->i_pcr_pid;
+    p_pmt->i_version = p_dvbpsipmt->i_version;
+
+    ValidateDVBMeta( p_demux, p_pmt->i_pid_pcr );
+
+    if( ProgramIsSelected( p_sys, p_pmt->i_number ) )
+        SetPIDFilter( p_sys, GetPID(p_sys, p_pmt->i_pid_pcr), true ); /* Set demux filter */
+
+    /* Parse PMT descriptors */
+    ts_pmt_registration_type_t registration_type = TS_PMT_REGISTRATION_NONE;
+    ParsePMTRegistrations( p_demux, p_dvbpsipmt->p_first_descriptor, p_pmt, &registration_type );
+
+    dvbpsi_pmt_es_t *p_dvbpsies;
+    for( p_dvbpsies = p_dvbpsipmt->p_first_es; p_dvbpsies != NULL; p_dvbpsies = p_dvbpsies->p_next )
+    {
+        /* Do not mix with arbitrary pid if any */
+        if( p_sys->b_atsc_eas && p_dvbpsies->i_pid == SCTE18_SI_BASE_PID )
+            continue;
+
+        ts_pid_t *pespid = GetPID(p_sys, p_dvbpsies->i_pid);
+        if ( pespid->type != TYPE_PES && pespid->type != TYPE_FREE )
+        {
+            msg_Warn( p_demux, " * PMT wants to create PES on pid %d used by non PES", pespid->i_pid );
+            continue;
+        }
+
+        ValidateDVBMeta( p_demux, p_dvbpsies->i_pid );
+
+        char const * psz_typedesc = ISO13818_1_Get_StreamType_Description( p_dvbpsies->i_type );
+
+        msg_Dbg( p_demux, "  * pid=%d type=0x%x %s",
+                 p_dvbpsies->i_pid, p_dvbpsies->i_type, psz_typedesc );
+
+        for( dvbpsi_descriptor_t *p_dr = p_dvbpsies->p_first_descriptor;
+             p_dr != NULL; p_dr = p_dr->p_next )
+        {
+            msg_Dbg( p_demux, "    - ES descriptor tag 0x%x",
+                     p_dr->i_tag );
+        }
+
+        const bool b_pid_inuse = ( pespid->type == TYPE_PES );
+        ts_pes_t *p_pes;
+
+        if ( !PIDSetup( p_demux, TYPE_PES, pespid, pmtpid ) )
+        {
+            msg_Warn( p_demux, "  * pid=%d type=0x%x %s (skipped)",
+                      p_dvbpsies->i_pid, p_dvbpsies->i_type, psz_typedesc );
+            continue;
+        }
+        else
+        {
+            if( b_pid_inuse ) /* pes will point to a temp */
+            {
+                p_pes = ts_pes_New( p_demux, p_pmt );
+                if( !p_pes )
+                {
+                    PIDRelease( p_demux, pespid );
+                    continue;
+                }
+            }
+            else  /* pes will point to the new one allocated from PIDSetup */
+            {
+                p_pes = pespid->u.p_pes;
+            }
+        }
+
+        /* Add pid to the list of used ones in pmt */
+        ARRAY_APPEND( p_pmt->e_streams, pespid );
+        pespid->i_flags |= SEEN(GetPID(p_sys, p_dvbpsies->i_pid));
+
+        /* Fill p_pes es and add extra es if any */
+        FillPESFromDvbpsiES( p_demux, p_dvbpsipmt, p_dvbpsies,
+                             registration_type, p_pmt, p_pes );
+
+        /* Set description and debug */
+        if( p_pes->p_es->fmt.i_cat == UNKNOWN_ES )
+        {
+            msg_Dbg( p_demux, "   => pid %d content is *unknown*",
+                     p_dvbpsies->i_pid );
+            p_pes->p_es->fmt.psz_description = strdup( psz_typedesc );
+        }
+        else
+        {
+            msg_Dbg( p_demux, "   => pid %d has now es fcc=%4.4s",
+                     p_dvbpsies->i_pid, (char*)&p_pes->p_es->fmt.i_codec );
+        }
+
+        dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x09 );
+        if( p_dr && p_dr->i_length >= 2 )
+        {
+            msg_Dbg( p_demux, "    - ES descriptor : CA (0x9) SysID 0x%x",
+                     (p_dr->p_data[0] << 8) | p_dr->p_data[1] );
+        }
+
+        const bool b_create_es = ((p_pes->p_es->fmt.i_cat == VIDEO_ES) ||
+                                  (p_pes->p_es->fmt.i_cat == AUDIO_ES));
+
+        /* Now check and merge */
+        if( b_pid_inuse ) /* We need to compare to the existing pes/es */
+        {
+            ts_pes_es_t *p_existing_es = ts_pes_Find_es( pespid->u.p_pes, p_pmt );
+            if( p_existing_es )
+            {
+                const es_format_t *ofmt = &p_existing_es->fmt;
+                const es_format_t *nfmt = &p_pes->p_es->fmt;
+
+                /* Check if we can avoid restarting that ES */
+                bool b_canreuse = es_format_IsSimilar( ofmt, nfmt );
+
+                /* Check codecs extra */
+                b_canreuse = b_canreuse &&
+                (
+                    ofmt->i_extra == nfmt->i_extra &&
+                    ( ofmt->i_extra == 0 ||
+                      memcmp( ofmt->p_extra, nfmt->p_extra, nfmt->i_extra ) == 0 )
+                );
+
+                /* Tracks must have same language */
+                b_canreuse = b_canreuse &&
+                (
+                    ( !!ofmt->psz_language == !!nfmt->psz_language ) &&
+                    ( ofmt->psz_language == NULL ||
+                      !strcmp( ofmt->psz_language, nfmt->psz_language ) )
+                );
+
+                /* Check is we have any subtitles */
+                b_canreuse = b_canreuse &&
+                ( ts_pes_Count_es( p_pes->p_es->p_extraes, false, NULL ) ==
+                  ts_pes_Count_es( p_existing_es->p_extraes, false, NULL )
+                );
+
+                if( b_canreuse )
+                {
+                    /* Just keep using previous es */
+                    ts_pes_Del( p_demux, p_pes );
+                }
+                else
+                {
+                    ts_pes_es_t *p_new = ts_pes_Extract_es( p_pes, p_pmt );
+                    ts_pes_Add_es( pespid->u.p_pes, p_new, false );
+                    assert(ts_pes_Count_es(p_pes->p_es, false, NULL) == 0);
+                    ts_pes_Del( p_demux, p_pes );
+                }
+            }
+            else /* There was no es for that program on that pid, merge in */
+            {
+                assert(ts_pes_Count_es(pespid->u.p_pes->p_es, false, NULL)); /* Used by another program */
+                ts_pes_es_t *p_new = ts_pes_Extract_es( p_pes, p_pmt );
+                assert( p_new );
+                ts_pes_Add_es( pespid->u.p_pes, p_new, false );
+                ts_pes_Del( p_demux, p_pes );
+            }
+        }
+        /* Nothing to do, pes is now just set */
+        if( b_create_es )
+            AddAndCreateES( p_demux, pespid, false );
+    }
+
+    /* Set CAM descrambling */
+    if( !ProgramIsSelected( p_sys, p_pmt->i_number ) )
+    {
+        dvbpsi_pmt_delete( p_dvbpsipmt );
+    }
+    else if( stream_Control( p_sys->stream, STREAM_SET_PRIVATE_ID_CA,
+                             p_dvbpsipmt ) != VLC_SUCCESS )
+    {
+        if ( p_sys->arib.e_mode == ARIBMODE_ENABLED && !p_sys->arib.b25stream )
+        {
+            p_sys->arib.b25stream = stream_FilterNew( p_demux->s, "aribcam" );
+            p_sys->stream = ( p_sys->arib.b25stream ) ? p_sys->arib.b25stream : p_demux->s;
+            if (!p_sys->arib.b25stream)
+                dvbpsi_pmt_delete( p_dvbpsipmt );
+        } else dvbpsi_pmt_delete( p_dvbpsipmt );
+    }
+
+     /* Add arbitrary PID from here */
+    if ( p_sys->b_atsc_eas && p_pmt->e_streams.i_size )
+    {
+        ts_pid_t *easpid = GetPID(p_sys, SCTE18_SI_BASE_PID);
+        if ( PIDSetup( p_demux, TYPE_PES, easpid, pmtpid ) )
+        {
+            ARRAY_APPEND( p_pmt->e_streams, easpid );
+            ts_pes_t *p_easpes = easpid->u.p_pes;
+            p_easpes->data_type = TS_ES_DATA_TABLE_SECTION;
+            p_easpes->p_es->fmt.i_codec = VLC_CODEC_SCTE_18;
+            p_easpes->p_es->fmt.i_cat = SPU_ES;
+            p_easpes->p_es->fmt.i_id = SCTE18_SI_BASE_PID;
+            p_easpes->p_es->fmt.i_group = p_pmt->i_number;
+            p_easpes->p_es->fmt.psz_description = strdup(SCTE18_DESCRIPTION);
+            p_easpes->b_always_receive = true;
+            ts_sections_processor_Add( &p_easpes->p_sections_proc,
+                                       SCTE18_TABLE_ID, 0x00,
+                                       false, SCTE18_Section_Handler );
+            msg_Dbg( p_demux, "  * pid=%d listening for EAS events", easpid->i_pid );
+        }
+    }
+
+    /* Decref or clean now unused es */
+    for( int i = 0; i < pid_to_decref.i_size; i++ )
+        PIDRelease( p_demux, pid_to_decref.p_elems[i] );
+    ARRAY_RESET( pid_to_decref );
+
+    UpdatePESFilters( p_demux, p_sys->b_es_all );
+
+    if( !p_sys->b_trust_pcr )
+    {
+        int i_cand = FindPCRCandidate( p_pmt );
+        p_pmt->i_pid_pcr = i_cand;
+        p_pmt->pcr.b_disable = true;
+        msg_Warn( p_demux, "PCR not trusted for program %d, set up workaround using pid %d",
+                  p_pmt->i_number, i_cand );
+    }
+
+    /* Probe Boundaries */
+    if( p_sys->b_canfastseek && p_pmt->i_last_dts == -1 )
+    {
+        p_pmt->i_last_dts = 0;
+        ProbeStart( p_demux, p_pmt->i_number );
+        ProbeEnd( p_demux, p_pmt->i_number );
+    }
+}
+
+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 *pmtpid = GetPID(p_sys, i_pid);
+
+    msg_Dbg( p_demux, "user pmt specified (pid=%d,number=%d)", i_pid, i_number );
+    if ( !PIDSetup( p_demux, TYPE_PMT, pmtpid, GetPID(p_sys, 0) ) )
+        goto error;
+
+    /* Dummy PMT */
+    ts_pmt_t *p_pmt = pmtpid->u.p_pmt;
+    p_pmt->i_number   = i_number != 0 ? i_number : TS_USER_PMT_NUMBER;
+    if( !dvbpsi_pmt_attach( p_pmt->handle,
+                            ((i_number != TS_USER_PMT_NUMBER ? i_number : 1)),
+                            PMTCallBack, p_demux ) )
+    {
+        PIDRelease( p_demux, pmtpid );
+        goto error;
+    }
+
+    ARRAY_APPEND( GetPID(p_sys, 0)->u.p_pat->programs, pmtpid );
+
+    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" ) )
+        {
+            p_pmt->i_pid_pcr = i_pid;
+        }
+        else if( GetPID(p_sys, i_pid)->type == TYPE_FREE )
+        {
+            ts_pid_t *pid = GetPID(p_sys, i_pid);
+
+            char *psz_arg = strchr( psz_opt, '=' );
+            if( psz_arg )
+                *psz_arg++ = '\0';
+
+            if ( !PIDSetup( p_demux, TYPE_PES, pid, pmtpid ) )
+                continue;
+
+            ARRAY_APPEND( p_pmt->e_streams, pid );
+
+            if( p_pmt->i_pid_pcr <= 0 )
+                p_pmt->i_pid_pcr = i_pid;
+
+            es_format_t *fmt = &pid->u.p_pes->p_es->fmt;
+
+            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;
+
+                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->u.p_pes, i_stream_type, &pid->u.p_pes->data_type );
+            }
+
+            fmt->i_group = i_number;
+            if( p_sys->b_es_id_pid )
+                fmt->i_id = i_pid;
+
+            if( fmt->i_cat != UNKNOWN_ES )
+            {
+                msg_Dbg( p_demux, "  * es pid=%d fcc=%4.4s", i_pid,
+                         (char*)&fmt->i_codec );
+                pid->u.p_pes->p_es->id = es_out_Add( p_demux->out, fmt );
+                p_sys->i_pmt_es++;
+            }
+        }
+
+    next:
+        psz = psz_next;
+    }
+
+    p_sys->b_user_pmt = true;
+    free( psz_dup );
+    return VLC_SUCCESS;
+
+error:
+    free( psz_dup );
+    return VLC_EGENERIC;
+}
diff --git a/modules/demux/mpeg/ts_psi.h b/modules/demux/mpeg/ts_psi.h
new file mode 100644
index 000000000000..9926ae444d77
--- /dev/null
+++ b/modules/demux/mpeg/ts_psi.h
@@ -0,0 +1,35 @@
+/*****************************************************************************
+ * ts_psi.h: Transport Stream input module for VLC.
+ *****************************************************************************
+ * Copyright (C) 2004-2016 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifndef VLC_TS_PSI_H
+#define VLC_TS_PSI_H
+
+typedef enum
+{
+    TS_PMT_REGISTRATION_NONE = 0,
+    TS_PMT_REGISTRATION_BLURAY,
+    TS_PMT_REGISTRATION_ATSC,
+    TS_PMT_REGISTRATION_ARIB,
+} ts_pmt_registration_type_t;
+
+void PATCallBack( void *, dvbpsi_pat_t * );
+void PMTCallBack( void *, dvbpsi_pmt_t * );
+
+int UserPmt( demux_t *p_demux, const char * );
+
+#endif
diff --git a/modules/demux/mpeg/ts_psi_eit.c b/modules/demux/mpeg/ts_psi_eit.c
new file mode 100644
index 000000000000..91a795bfcee0
--- /dev/null
+++ b/modules/demux/mpeg/ts_psi_eit.c
@@ -0,0 +1,554 @@
+/*****************************************************************************
+ * ts_psi_eit.c : TS demuxer EIT handling
+ *****************************************************************************
+ * Copyright (C) 2014-2016 - VideoLAN Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_demux.h>
+#include <vlc_meta.h>
+#include <vlc_epg.h>
+#include <vlc_charset.h>   /* FromCharset, for EIT */
+
+#ifndef _DVBPSI_DVBPSI_H_
+ #include <dvbpsi/dvbpsi.h>
+#endif
+#include <dvbpsi/demux.h>
+#include <dvbpsi/descriptor.h>
+#include <dvbpsi/sdt.h>
+#include <dvbpsi/eit.h> /* EIT support */
+#include <dvbpsi/tot.h> /* TDT support */
+#include <dvbpsi/dr.h>
+
+#include "ts_psi_eit.h"
+
+#include "ts_pid.h"
+#include "ts_streams_private.h"
+#include "ts.h"
+#include "../dvb-text.h"
+
+#ifdef HAVE_ARIBB24
+ #include <aribb24/decoder.h>
+#endif
+
+#include <time.h>
+#include <assert.h>
+
+static char *EITConvertToUTF8( demux_t *p_demux,
+                               const unsigned char *psz_instring,
+                               size_t i_length,
+                               bool b_broken )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+#ifdef HAVE_ARIBB24
+    if( p_sys->arib.e_mode == ARIBMODE_ENABLED )
+    {
+        if ( !p_sys->arib.p_instance )
+            p_sys->arib.p_instance = arib_instance_new( p_demux );
+        if ( !p_sys->arib.p_instance )
+            return NULL;
+        arib_decoder_t *p_decoder = arib_get_decoder( p_sys->arib.p_instance );
+        if ( !p_decoder )
+            return NULL;
+
+        char *psz_outstring = NULL;
+        size_t i_out;
+
+        i_out = i_length * 4;
+        psz_outstring = (char*) calloc( i_out + 1, sizeof(char) );
+        if( !psz_outstring )
+            return NULL;
+
+        arib_initialize_decoder( p_decoder );
+        i_out = arib_decode_buffer( p_decoder, psz_instring, i_length,
+                                    psz_outstring, i_out );
+        arib_finalize_decoder( p_decoder );
+
+        return psz_outstring;
+    }
+#else
+    VLC_UNUSED(p_sys);
+#endif
+    /* Deal with no longer broken providers (no switch byte
+      but sending ISO_8859-1 instead of ISO_6937) without
+      removing them from the broken providers table
+      (keep the entry for correctly handling recorded TS).
+    */
+    b_broken = b_broken && i_length && *psz_instring > 0x20;
+
+    if( b_broken )
+        return FromCharset( "ISO_8859-1", psz_instring, i_length );
+    return vlc_from_EIT( psz_instring, i_length );
+}
+
+static void SDTCallBack( demux_t *p_demux, dvbpsi_sdt_t *p_sdt )
+{
+    demux_sys_t          *p_sys = p_demux->p_sys;
+    ts_pid_t             *sdt = GetPID(p_sys, 0x11);
+    dvbpsi_sdt_service_t *p_srv;
+
+    msg_Dbg( p_demux, "SDTCallBack called" );
+
+    if( p_sys->es_creation != CREATE_ES ||
+       !p_sdt->b_current_next ||
+        p_sdt->i_version == sdt->u.p_psi->i_version )
+    {
+        dvbpsi_sdt_delete( p_sdt );
+        return;
+    }
+
+    msg_Dbg( p_demux, "new SDT ts_id=%d version=%d current_next=%d "
+             "network_id=%d",
+             p_sdt->i_extension,
+             p_sdt->i_version, p_sdt->b_current_next,
+             p_sdt->i_network_id );
+
+    p_sys->b_broken_charset = false;
+
+    for( p_srv = p_sdt->p_first_service; p_srv; p_srv = p_srv->p_next )
+    {
+        vlc_meta_t          *p_meta;
+        dvbpsi_descriptor_t *p_dr;
+
+        const char *psz_type = NULL;
+        const char *psz_status = NULL;
+
+        msg_Dbg( p_demux, "  * service id=%d eit schedule=%d present=%d "
+                 "running=%d free_ca=%d",
+                 p_srv->i_service_id, p_srv->b_eit_schedule,
+                 p_srv->b_eit_present, p_srv->i_running_status,
+                 p_srv->b_free_ca );
+
+        if( p_sys->vdr.i_service && p_srv->i_service_id != p_sys->vdr.i_service )
+        {
+            msg_Dbg( p_demux, "  * service id=%d skipped (not declared in vdr header)",
+                     p_sys->vdr.i_service );
+            continue;
+        }
+
+        p_meta = vlc_meta_New();
+        for( p_dr = p_srv->p_first_descriptor; p_dr; p_dr = p_dr->p_next )
+        {
+            if( p_dr->i_tag == 0x48 )
+            {
+                static const char *ppsz_type[17] = {
+                    "Reserved",
+                    "Digital television service",
+                    "Digital radio sound service",
+                    "Teletext service",
+                    "NVOD reference service",
+                    "NVOD time-shifted service",
+                    "Mosaic service",
+                    "PAL coded signal",
+                    "SECAM coded signal",
+                    "D/D2-MAC",
+                    "FM Radio",
+                    "NTSC coded signal",
+                    "Data broadcast service",
+                    "Reserved for Common Interface Usage",
+                    "RCS Map (see EN 301 790 [35])",
+                    "RCS FLS (see EN 301 790 [35])",
+                    "DVB MHP service"
+                };
+                dvbpsi_service_dr_t *pD = dvbpsi_DecodeServiceDr( p_dr );
+                char *str1 = NULL;
+                char *str2 = NULL;
+
+                /* Workarounds for broadcasters with broken EPG */
+
+                if( p_sdt->i_network_id == 133 )
+                    p_sys->b_broken_charset = true;  /* SKY DE & BetaDigital use ISO8859-1 */
+
+                /* List of providers using ISO8859-1 */
+                static const char ppsz_broken_providers[][8] = {
+                    "CSAT",     /* CanalSat FR */
+                    "GR1",      /* France televisions */
+                    "MULTI4",   /* NT1 */
+                    "MR5",      /* France 2/M6 HD */
+                    ""
+                };
+                for( int i = 0; *ppsz_broken_providers[i]; i++ )
+                {
+                    const size_t i_length = strlen(ppsz_broken_providers[i]);
+                    if( pD->i_service_provider_name_length == i_length &&
+                        !strncmp( (char *)pD->i_service_provider_name, ppsz_broken_providers[i], i_length ) )
+                        p_sys->b_broken_charset = true;
+                }
+
+                /* FIXME: Digital+ ES also uses ISO8859-1 */
+
+                str1 = EITConvertToUTF8(p_demux,
+                                        pD->i_service_provider_name,
+                                        pD->i_service_provider_name_length,
+                                        p_sys->b_broken_charset );
+                str2 = EITConvertToUTF8(p_demux,
+                                        pD->i_service_name,
+                                        pD->i_service_name_length,
+                                        p_sys->b_broken_charset );
+
+                msg_Dbg( p_demux, "    - type=%d provider=%s name=%s",
+                         pD->i_service_type, str1, str2 );
+
+                vlc_meta_SetTitle( p_meta, str2 );
+                vlc_meta_SetPublisher( p_meta, str1 );
+                if( pD->i_service_type >= 0x01 && pD->i_service_type <= 0x10 )
+                    psz_type = ppsz_type[pD->i_service_type];
+                free( str1 );
+                free( str2 );
+            }
+        }
+
+        if( p_srv->i_running_status >= 0x01 && p_srv->i_running_status <= 0x04 )
+        {
+            static const char *ppsz_status[5] = {
+                "Unknown",
+                "Not running",
+                "Starts in a few seconds",
+                "Pausing",
+                "Running"
+            };
+            psz_status = ppsz_status[p_srv->i_running_status];
+        }
+
+        if( psz_type )
+            vlc_meta_AddExtra( p_meta, "Type", psz_type );
+        if( psz_status )
+            vlc_meta_AddExtra( p_meta, "Status", psz_status );
+
+        es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META,
+                        p_srv->i_service_id, p_meta );
+        vlc_meta_Delete( p_meta );
+    }
+
+    sdt->u.p_psi->i_version = p_sdt->i_version;
+    dvbpsi_sdt_delete( p_sdt );
+}
+
+static void EITDecodeMjd( int i_mjd, int *p_y, int *p_m, int *p_d )
+{
+    const int yp = (int)( ( (double)i_mjd - 15078.2)/365.25 );
+    const int mp = (int)( ((double)i_mjd - 14956.1 - (int)(yp * 365.25)) / 30.6001 );
+    const int c = ( mp == 14 || mp == 15 ) ? 1 : 0;
+
+    *p_y = 1900 + yp + c*1;
+    *p_m = mp - 1 - c*12;
+    *p_d = i_mjd - 14956 - (int)(yp*365.25) - (int)(mp*30.6001);
+}
+#define CVT_FROM_BCD(v) ((((v) >> 4)&0xf)*10 + ((v)&0xf))
+static int64_t EITConvertStartTime( uint64_t i_date )
+{
+    const int i_mjd = i_date >> 24;
+    struct tm tm;
+
+    tm.tm_hour = CVT_FROM_BCD(i_date >> 16);
+    tm.tm_min  = CVT_FROM_BCD(i_date >>  8);
+    tm.tm_sec  = CVT_FROM_BCD(i_date      );
+
+    /* if all 40 bits are 1, the start is unknown */
+    if( i_date == UINT64_C(0xffffffffff) )
+        return -1;
+
+    EITDecodeMjd( i_mjd, &tm.tm_year, &tm.tm_mon, &tm.tm_mday );
+    tm.tm_year -= 1900;
+    tm.tm_mon--;
+    tm.tm_isdst = 0;
+
+    return timegm( &tm );
+}
+static int EITConvertDuration( uint32_t i_duration )
+{
+    return CVT_FROM_BCD(i_duration >> 16) * 3600 +
+           CVT_FROM_BCD(i_duration >> 8 ) * 60 +
+           CVT_FROM_BCD(i_duration      );
+}
+#undef CVT_FROM_BCD
+
+static void TDTCallBack( demux_t *p_demux, dvbpsi_tot_t *p_tdt )
+{
+    demux_sys_t        *p_sys = p_demux->p_sys;
+
+    p_sys->i_tdt_delta = CLOCK_FREQ * EITConvertStartTime( p_tdt->i_utc_time )
+                         - mdate();
+    dvbpsi_tot_delete(p_tdt);
+}
+
+
+static void EITCallBack( demux_t *p_demux,
+                         dvbpsi_eit_t *p_eit, bool b_current_following )
+{
+    demux_sys_t        *p_sys = p_demux->p_sys;
+    dvbpsi_eit_event_t *p_evt;
+    vlc_epg_t *p_epg;
+
+    msg_Dbg( p_demux, "EITCallBack called" );
+    if( !p_eit->b_current_next )
+    {
+        dvbpsi_eit_delete( p_eit );
+        return;
+    }
+
+    msg_Dbg( p_demux, "new EIT service_id=%d version=%d current_next=%d "
+             "ts_id=%d network_id=%d segment_last_section_number=%d "
+             "last_table_id=%d",
+             p_eit->i_extension,
+             p_eit->i_version, p_eit->b_current_next,
+             p_eit->i_ts_id, p_eit->i_network_id,
+             p_eit->i_segment_last_section_number, p_eit->i_last_table_id );
+
+    p_epg = vlc_epg_New( NULL );
+    for( p_evt = p_eit->p_first_event; p_evt; p_evt = p_evt->p_next )
+    {
+        dvbpsi_descriptor_t *p_dr;
+        char                *psz_name = NULL;
+        char                *psz_text = NULL;
+        char                *psz_extra = strdup("");
+        int64_t i_start;
+        int i_duration;
+        int i_min_age = 0;
+        int64_t i_tot_time = 0;
+
+        i_start = EITConvertStartTime( p_evt->i_start_time );
+        i_duration = EITConvertDuration( p_evt->i_duration );
+
+        if( p_sys->arib.e_mode == ARIBMODE_ENABLED )
+        {
+            if( p_sys->i_tdt_delta == 0 )
+                p_sys->i_tdt_delta = CLOCK_FREQ * (i_start + i_duration - 5) - mdate();
+
+            i_tot_time = (mdate() + p_sys->i_tdt_delta) / CLOCK_FREQ;
+
+            tzset(); // JST -> UTC
+            i_start += timezone; // FIXME: what about DST?
+            i_tot_time += timezone;
+
+            if( p_evt->i_running_status == 0x00 &&
+                (i_start - 5 < i_tot_time &&
+                 i_tot_time < i_start + i_duration + 5) )
+            {
+                p_evt->i_running_status = 0x04;
+                msg_Dbg( p_demux, "  EIT running status 0x00 -> 0x04" );
+            }
+        }
+
+        msg_Dbg( p_demux, "  * event id=%d start_time:%d duration=%d "
+                          "running=%d free_ca=%d",
+                 p_evt->i_event_id, (int)i_start, (int)i_duration,
+                 p_evt->i_running_status, p_evt->b_free_ca );
+
+        for( p_dr = p_evt->p_first_descriptor; p_dr; p_dr = p_dr->p_next )
+        {
+            switch(p_dr->i_tag)
+            {
+            case 0x4d:
+            {
+                dvbpsi_short_event_dr_t *pE = dvbpsi_DecodeShortEventDr( p_dr );
+
+                /* Only take first description, as we don't handle language-info
+                   for epg atm*/
+                if( pE && psz_name == NULL )
+                {
+                    psz_name = EITConvertToUTF8( p_demux,
+                                                 pE->i_event_name, pE->i_event_name_length,
+                                                 p_sys->b_broken_charset );
+                    free( psz_text );
+                    psz_text = EITConvertToUTF8( p_demux,
+                                                 pE->i_text, pE->i_text_length,
+                                                 p_sys->b_broken_charset );
+                    msg_Dbg( p_demux, "    - short event lang=%3.3s '%s' : '%s'",
+                             pE->i_iso_639_code, psz_name, psz_text );
+                }
+            }
+                break;
+
+            case 0x4e:
+            {
+                dvbpsi_extended_event_dr_t *pE = dvbpsi_DecodeExtendedEventDr( p_dr );
+                if( pE )
+                {
+                    msg_Dbg( p_demux, "    - extended event lang=%3.3s [%d/%d]",
+                             pE->i_iso_639_code,
+                             pE->i_descriptor_number, pE->i_last_descriptor_number );
+
+                    if( pE->i_text_length > 0 )
+                    {
+                        char *psz_text = EITConvertToUTF8( p_demux,
+                                                           pE->i_text, pE->i_text_length,
+                                                           p_sys->b_broken_charset );
+                        if( psz_text )
+                        {
+                            msg_Dbg( p_demux, "       - text='%s'", psz_text );
+
+                            psz_extra = xrealloc( psz_extra,
+                                   strlen(psz_extra) + strlen(psz_text) + 1 );
+                            strcat( psz_extra, psz_text );
+                            free( psz_text );
+                        }
+                    }
+
+                    for( int i = 0; i < pE->i_entry_count; i++ )
+                    {
+                        char *psz_dsc = EITConvertToUTF8( p_demux,
+                                                          pE->i_item_description[i],
+                                                          pE->i_item_description_length[i],
+                                                          p_sys->b_broken_charset );
+                        char *psz_itm = EITConvertToUTF8( p_demux,
+                                                          pE->i_item[i], pE->i_item_length[i],
+                                                          p_sys->b_broken_charset );
+
+                        if( psz_dsc && psz_itm )
+                        {
+                            msg_Dbg( p_demux, "       - desc='%s' item='%s'", psz_dsc, psz_itm );
+#if 0
+                            psz_extra = xrealloc( psz_extra,
+                                         strlen(psz_extra) + strlen(psz_dsc) +
+                                         strlen(psz_itm) + 3 + 1 );
+                            strcat( psz_extra, "(" );
+                            strcat( psz_extra, psz_dsc );
+                            strcat( psz_extra, " " );
+                            strcat( psz_extra, psz_itm );
+                            strcat( psz_extra, ")" );
+#endif
+                        }
+                        free( psz_dsc );
+                        free( psz_itm );
+                    }
+                }
+            }
+                break;
+
+            case 0x55:
+            {
+                dvbpsi_parental_rating_dr_t *pR = dvbpsi_DecodeParentalRatingDr( p_dr );
+                if ( pR )
+                {
+                    for ( int i = 0; i < pR->i_ratings_number; i++ )
+                    {
+                        const dvbpsi_parental_rating_t *p_rating = & pR->p_parental_rating[ i ];
+                        if ( p_rating->i_rating > 0x00 && p_rating->i_rating <= 0x0F )
+                        {
+                            if ( p_rating->i_rating + 3 > i_min_age )
+                                i_min_age = p_rating->i_rating + 3;
+                            msg_Dbg( p_demux, "    - parental control set to %d years",
+                                     i_min_age );
+                        }
+                    }
+                }
+            }
+                break;
+
+            default:
+                msg_Dbg( p_demux, "    - event unknown dr 0x%x(%d)", p_dr->i_tag, p_dr->i_tag );
+                break;
+            }
+        }
+
+        /* */
+        if( i_start > 0 && psz_name && psz_text)
+            vlc_epg_AddEvent( p_epg, i_start, i_duration, psz_name, psz_text,
+                              *psz_extra ? psz_extra : NULL, i_min_age );
+
+        /* Update "now playing" field */
+        if( p_evt->i_running_status == 0x04 && i_start > 0  && psz_name && psz_text )
+            vlc_epg_SetCurrent( p_epg, i_start );
+
+        free( psz_name );
+        free( psz_text );
+
+        free( psz_extra );
+    }
+    if( p_epg->i_event > 0 )
+    {
+        if( b_current_following &&
+            (  p_sys->programs.i_size == 0 ||
+               p_sys->programs.p_elems[0] ==
+                    p_eit->i_extension
+                ) )
+        {
+            p_sys->i_dvb_length = 0;
+            p_sys->i_dvb_start = 0;
+
+            if( p_epg->p_current )
+            {
+                p_sys->i_dvb_start = CLOCK_FREQ * p_epg->p_current->i_start;
+                p_sys->i_dvb_length = CLOCK_FREQ * p_epg->p_current->i_duration;
+            }
+        }
+        es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG,
+                        p_eit->i_extension,
+                        p_epg );
+    }
+    vlc_epg_Delete( p_epg );
+
+    dvbpsi_eit_delete( p_eit );
+}
+static void EITCallBackCurrentFollowing( demux_t *p_demux, dvbpsi_eit_t *p_eit )
+{
+    EITCallBack( p_demux, p_eit, true );
+}
+static void EITCallBackSchedule( demux_t *p_demux, dvbpsi_eit_t *p_eit )
+{
+    EITCallBack( p_demux, p_eit, false );
+}
+
+static void PSINewTableCallBack( dvbpsi_t *h, uint8_t i_table_id,
+                                 uint16_t i_extension, demux_t *p_demux )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    assert( h );
+#if 0
+    msg_Dbg( p_demux, "PSINewTableCallBack: table 0x%x(%d) ext=0x%x(%d)",
+             i_table_id, i_table_id, i_extension, i_extension );
+#endif
+    if( GetPID(p_sys, 0)->u.p_pat->i_version != -1 && i_table_id == 0x42 )
+    {
+        msg_Dbg( p_demux, "PSINewTableCallBack: table 0x%x(%d) ext=0x%x(%d)",
+                 i_table_id, i_table_id, i_extension, i_extension );
+
+        if( !dvbpsi_sdt_attach( h, i_table_id, i_extension, (dvbpsi_sdt_callback)SDTCallBack, p_demux ) )
+            msg_Err( p_demux, "PSINewTableCallback: failed attaching SDTCallback" );
+    }
+    else if( GetPID(p_sys, 0x11)->u.p_psi->i_version != -1 &&
+             ( i_table_id == 0x4e || /* Current/Following */
+               (i_table_id >= 0x50 && i_table_id <= 0x5f) ) ) /* Schedule */
+    {
+        msg_Dbg( p_demux, "PSINewTableCallBack: table 0x%x(%d) ext=0x%x(%d)",
+                 i_table_id, i_table_id, i_extension, i_extension );
+
+        dvbpsi_eit_callback cb = i_table_id == 0x4e ?
+                                    (dvbpsi_eit_callback)EITCallBackCurrentFollowing :
+                                    (dvbpsi_eit_callback)EITCallBackSchedule;
+
+        if( !dvbpsi_eit_attach( h, i_table_id, i_extension, cb, p_demux ) )
+            msg_Err( p_demux, "PSINewTableCallback: failed attaching EITCallback" );
+    }
+    else if( GetPID(p_sys, 0x11)->u.p_psi->i_version != -1 &&
+            (i_table_id == 0x70 /* TDT */ || i_table_id == 0x73 /* TOT */) )
+    {
+         msg_Dbg( p_demux, "PSINewTableCallBack: table 0x%x(%d) ext=0x%x(%d)",
+                 i_table_id, i_table_id, i_extension, i_extension );
+
+        if( !dvbpsi_tot_attach( h, i_table_id, i_extension, (dvbpsi_tot_callback)TDTCallBack, p_demux ) )
+            msg_Err( p_demux, "PSINewTableCallback: failed attaching TDTCallback" );
+    }
+}
+
+bool AttachDvbpsiNewEITTableHandler( dvbpsi_t *p_handle, demux_t * p_demux )
+{
+    return dvbpsi_AttachDemux( p_handle, (dvbpsi_demux_new_cb_t)PSINewTableCallBack, p_demux );
+}
diff --git a/modules/demux/mpeg/ts_psi_eit.h b/modules/demux/mpeg/ts_psi_eit.h
new file mode 100644
index 000000000000..726eb886aae0
--- /dev/null
+++ b/modules/demux/mpeg/ts_psi_eit.h
@@ -0,0 +1,24 @@
+/*****************************************************************************
+ * ts_psi_eit.h : TS demuxer EIT handling
+ *****************************************************************************
+ * Copyright (C) 2014-2016 - VideoLAN Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifndef VLC_TS_PSI_EIT_H
+#define VLC_TS_PSI_EIT_H
+
+bool AttachDvbpsiNewEITTableHandler( dvbpsi_t *p_handle, demux_t * p_demux );
+
+#endif
diff --git a/modules/demux/mpeg/ts_scte.c b/modules/demux/mpeg/ts_scte.c
new file mode 100644
index 000000000000..ba8765841e7b
--- /dev/null
+++ b/modules/demux/mpeg/ts_scte.c
@@ -0,0 +1,91 @@
+/*****************************************************************************
+ * ts_scte.c: TS Demux SCTE section decoders/handlers
+ *****************************************************************************
+ * Copyright (C) 2004-2016 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_demux.h>
+#include <vlc_es.h>
+
+#include "ts_pid.h"
+#include "ts_scte.h"
+#include "ts_streams_private.h"
+#include "timestamps.h"
+
+#include <assert.h>
+
+void SCTE18_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content )
+{
+    assert( pid->u.p_pes->p_es->fmt.i_codec == VLC_CODEC_SCTE_18 );
+    ts_pmt_t *p_pmt = pid->u.p_pes->p_es->p_program;
+    mtime_t i_date = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->pcr.i_current );
+
+    int i_priority = scte18_get_EAS_priority( p_content->p_buffer, p_content->i_buffer );
+    msg_Dbg( p_demux, "Received EAS Alert with priority %d", i_priority );
+    /* We need to extract the truncated pts stored inside the payload */
+    ts_pes_es_t *p_es = pid->u.p_pes->p_es;
+    if( p_es->id )
+    {
+        if( i_priority == EAS_PRIORITY_HIGH || i_priority == EAS_PRIORITY_MAX )
+            es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, p_es->id, true );
+        p_content->i_dts = p_content->i_pts = FROM_SCALE( i_date );
+        es_out_Send( p_demux->out, p_es->id, p_content );
+    }
+    else
+        block_Release( p_content );
+}
+
+void SCTE27_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content )
+{
+    assert( pid->u.p_pes->p_es->fmt.i_codec == VLC_CODEC_SCTE_27 );
+    ts_pmt_t *p_pmt = pid->u.p_pes->p_es->p_program;
+    mtime_t i_date = p_pmt->pcr.i_current;
+
+    /* We need to extract the truncated pts stored inside the payload */
+    int i_index = 0;
+    size_t i_offset = 4;
+    if( p_content->p_buffer[3] & 0x40 )
+    {
+        i_index = ((p_content->p_buffer[7] & 0x0f) << 8) | /* segment number */
+                p_content->p_buffer[8];
+        i_offset += 5;
+    }
+    if( i_index == 0 && p_content->i_buffer > i_offset + 8 ) /* message body */
+    {
+        bool is_immediate = p_content->p_buffer[i_offset + 3] & 0x40;
+        if( !is_immediate )
+        {
+            mtime_t i_display_in = GetDWBE( &p_content->p_buffer[i_offset + 4] );
+            if( i_display_in < i_date )
+                i_date = i_display_in + (1ll << 32);
+            else
+                i_date = i_display_in;
+        }
+
+    }
+
+    p_content->i_dts = p_content->i_pts = VLC_TS_0 + i_date * 100 / 9;
+    //PCRFixHandle( p_demux, p_pmt, p_content );
+
+    if( pid->u.p_pes->p_es->id )
+        es_out_Send( p_demux->out, pid->u.p_pes->p_es->id, p_content );
+    else
+        block_Release( p_content );
+}
diff --git a/modules/demux/mpeg/ts_scte.h b/modules/demux/mpeg/ts_scte.h
new file mode 100644
index 000000000000..c60d644f8034
--- /dev/null
+++ b/modules/demux/mpeg/ts_scte.h
@@ -0,0 +1,27 @@
+/*****************************************************************************
+ * ts_scte.h: TS Demux SCTE section decoders/handlers
+ *****************************************************************************
+ * Copyright (C) 2004-2016 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifndef VLC_TS_SCTE_H
+#define VLC_TS_SCTE_H
+
+#include "../../codec/scte18.h"
+
+void SCTE18_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content );
+void SCTE27_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content );
+
+#endif
diff --git a/modules/demux/mpeg/ts_sl.c b/modules/demux/mpeg/ts_sl.c
new file mode 100644
index 000000000000..b55b927fa38a
--- /dev/null
+++ b/modules/demux/mpeg/ts_sl.c
@@ -0,0 +1,204 @@
+/*****************************************************************************
+ * ts_sl.c : MPEG SL/FMC handling for TS demuxer
+ *****************************************************************************
+ * Copyright (C) 2014-2016 - VideoLAN Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_demux.h>
+
+#include "ts_streams.h"
+#include "ts_pid.h"
+#include "ts_streams_private.h"
+#include "ts.h"
+
+#include "ts_sl.h"
+
+const es_mpeg4_descriptor_t * GetMPEG4DescByEsId( const ts_pmt_t *pmt, uint16_t i_es_id )
+{
+    for( int i = 0; i < ES_DESCRIPTOR_COUNT; i++ )
+    {
+        const es_mpeg4_descriptor_t *es_descr = &pmt->iod->es_descr[i];
+        if( es_descr->i_es_id == i_es_id && es_descr->b_ok )
+            return es_descr;
+    }
+    for( int i=0; i<pmt->od.objects.i_size; i++ )
+    {
+        const od_descriptor_t *od = pmt->od.objects.p_elems[i];
+        for( int j = 0; j < ES_DESCRIPTOR_COUNT; j++ )
+        {
+            const es_mpeg4_descriptor_t *es_descr = &od->es_descr[j];
+            if( es_descr->i_es_id == i_es_id && es_descr->b_ok )
+                return es_descr;
+        }
+    }
+    return NULL;
+}
+
+static ts_pes_es_t * GetPMTESBySLEsId( ts_pmt_t *pmt, uint16_t i_sl_es_id )
+{
+    for( int i=0; i< pmt->e_streams.i_size; i++ )
+    {
+        ts_pes_es_t *p_es = pmt->e_streams.p_elems[i]->u.p_pes->p_es;
+        if( p_es->i_sl_es_id == i_sl_es_id )
+            return p_es;
+    }
+    return NULL;
+}
+
+bool SetupISO14496LogicalStream( demux_t *p_demux, const decoder_config_descriptor_t *dcd,
+                                  es_format_t *p_fmt )
+{
+    msg_Dbg( p_demux, "     - IOD objecttype: %"PRIx8" streamtype:%"PRIx8,
+             dcd->i_objectTypeIndication, dcd->i_streamType );
+
+    if( dcd->i_streamType == 0x04 )    /* VisualStream */
+    {
+        p_fmt->i_cat = VIDEO_ES;
+        switch( dcd->i_objectTypeIndication )
+        {
+        case 0x0B: /* mpeg4 sub */
+            p_fmt->i_cat = SPU_ES;
+            p_fmt->i_codec = VLC_CODEC_SUBT;
+            break;
+
+        case 0x20: /* mpeg4 */
+            p_fmt->i_codec = VLC_CODEC_MP4V;
+            break;
+        case 0x21: /* h264 */
+            p_fmt->i_codec = VLC_CODEC_H264;
+            break;
+        case 0x60:
+        case 0x61:
+        case 0x62:
+        case 0x63:
+        case 0x64:
+        case 0x65: /* mpeg2 */
+            p_fmt->i_codec = VLC_CODEC_MPGV;
+            break;
+        case 0x6a: /* mpeg1 */
+            p_fmt->i_codec = VLC_CODEC_MPGV;
+            break;
+        case 0x6c: /* mpeg1 */
+            p_fmt->i_codec = VLC_CODEC_JPEG;
+            break;
+        default:
+            p_fmt->i_cat = UNKNOWN_ES;
+            break;
+        }
+    }
+    else if( dcd->i_streamType == 0x05 )    /* AudioStream */
+    {
+        p_fmt->i_cat = AUDIO_ES;
+        switch( dcd->i_objectTypeIndication )
+        {
+        case 0x40: /* mpeg4 */
+            p_fmt->i_codec = VLC_CODEC_MP4A;
+            break;
+        case 0x66:
+        case 0x67:
+        case 0x68: /* mpeg2 aac */
+            p_fmt->i_codec = VLC_CODEC_MP4A;
+            break;
+        case 0x69: /* mpeg2 */
+            p_fmt->i_codec = VLC_CODEC_MPGA;
+            break;
+        case 0x6b: /* mpeg1 */
+            p_fmt->i_codec = VLC_CODEC_MPGA;
+            break;
+        default:
+            p_fmt->i_cat = UNKNOWN_ES;
+            break;
+        }
+    }
+    else
+    {
+        p_fmt->i_cat = UNKNOWN_ES;
+    }
+
+    if( p_fmt->i_cat != UNKNOWN_ES )
+    {
+        p_fmt->i_extra = __MIN(dcd->i_extra, INT32_MAX);
+        if( p_fmt->i_extra > 0 )
+        {
+            p_fmt->p_extra = malloc( p_fmt->i_extra );
+            if( p_fmt->p_extra )
+                memcpy( p_fmt->p_extra, dcd->p_extra, p_fmt->i_extra );
+            else
+                p_fmt->i_extra = 0;
+        }
+    }
+
+    return true;
+}
+
+/* Object stream SL in table sections */
+void SLPackets_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content )
+{
+    ts_pmt_t *p_pmt = pid->u.p_pes->p_es->p_program;
+
+    const es_mpeg4_descriptor_t *p_mpeg4desc = GetMPEG4DescByEsId( p_pmt, pid->u.p_pes->p_es->i_sl_es_id );
+    if( p_mpeg4desc && p_mpeg4desc->dec_descr.i_objectTypeIndication == 0x01 &&
+        p_mpeg4desc->dec_descr.i_streamType == 0x01 /* Object */ )
+    {
+        const uint8_t *p_data = p_content->p_buffer;
+        int i_data = p_content->i_buffer;
+
+        od_descriptors_t *p_ods = &p_pmt->od;
+        sl_header_data header = DecodeSLHeader( i_data, p_data, &p_mpeg4desc->sl_descr );
+
+        DecodeODCommand( VLC_OBJECT(p_demux), p_ods, i_data - header.i_size, &p_data[header.i_size] );
+        bool b_changed = false;
+
+        for( int i=0; i<p_ods->objects.i_size; i++ )
+        {
+            od_descriptor_t *p_od = p_ods->objects.p_elems[i];
+            for( int j = 0; j < ES_DESCRIPTOR_COUNT && p_od->es_descr[j].b_ok; j++ )
+            {
+                p_mpeg4desc = &p_od->es_descr[j];
+                ts_pes_es_t *p_es = GetPMTESBySLEsId( p_pmt, p_mpeg4desc->i_es_id );
+                es_format_t fmt;
+                es_format_Init( &fmt, UNKNOWN_ES, 0 );
+                fmt.i_id = p_es->fmt.i_id;
+                fmt.i_group = p_es->fmt.i_group;
+
+                if ( p_mpeg4desc && p_mpeg4desc->b_ok && p_es &&
+                     SetupISO14496LogicalStream( p_demux, &p_mpeg4desc->dec_descr, &fmt ) &&
+                     !es_format_IsSimilar( &fmt, &p_es->fmt ) )
+                {
+                    es_format_Clean( &p_es->fmt );
+                    p_es->fmt = fmt;
+
+                    if( p_es->id )
+                        es_out_Del( p_demux->out, p_es->id );
+                    p_es->fmt.b_packetized = true; /* Split by access unit, no sync code */
+                    FREENULL( p_es->fmt.psz_description );
+                    p_es->id = es_out_Add( p_demux->out, &p_es->fmt );
+                    b_changed = true;
+                }
+            }
+        }
+
+        if( b_changed )
+            UpdatePESFilters( p_demux, p_demux->p_sys->b_es_all );
+    }
+
+    block_Release( p_content );
+}
+
diff --git a/modules/demux/mpeg/ts_sl.h b/modules/demux/mpeg/ts_sl.h
new file mode 100644
index 000000000000..414bcda0cfae
--- /dev/null
+++ b/modules/demux/mpeg/ts_sl.h
@@ -0,0 +1,29 @@
+/*****************************************************************************
+ * ts_sl.h : MPEG SL/FMC handling for TS demuxer
+ *****************************************************************************
+ * Copyright (C) 2014-2016 - VideoLAN Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifndef VLC_TS_SL_H
+#define VLC_TS_SL_H
+
+typedef struct es_mpeg4_descriptor_t es_mpeg4_descriptor_t;
+typedef struct decoder_config_descriptor_t decoder_config_descriptor_t;
+
+const es_mpeg4_descriptor_t * GetMPEG4DescByEsId( const ts_pmt_t *pmt, uint16_t i_es_id );
+void SLPackets_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content );
+bool SetupISO14496LogicalStream( demux_t *, const decoder_config_descriptor_t *,
+                                  es_format_t * );
+#endif
diff --git a/modules/demux/mpeg/ts_streams.c b/modules/demux/mpeg/ts_streams.c
new file mode 100644
index 000000000000..f6b7e438c011
--- /dev/null
+++ b/modules/demux/mpeg/ts_streams.c
@@ -0,0 +1,305 @@
+/*****************************************************************************
+ * ts_streams.c: Transport Stream input module for VLC.
+ *****************************************************************************
+ * Copyright (C) 2004-2016 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+
+#include "ts_pid.h"
+#include "ts_streams.h"
+#include "ts_streams_private.h"
+
+#ifndef _DVBPSI_DVBPSI_H_
+ #include <dvbpsi/dvbpsi.h>
+#endif
+#include <dvbpsi/demux.h>
+#include <dvbpsi/descriptor.h>
+#include <dvbpsi/pat.h>
+#include <dvbpsi/pmt.h>
+#include "../../mux/mpeg/dvbpsi_compat.h" /* dvbpsi_messages */
+
+#include <vlc_demux.h>
+#include <vlc_es.h>
+#include <vlc_es_out.h>
+
+#include "sections.h"
+#include "ts_pid.h"
+#include "ts.h"
+
+static inline bool handle_Init( demux_t *p_demux, dvbpsi_t **handle )
+{
+    *handle = dvbpsi_new( &dvbpsi_messages, DVBPSI_MSG_DEBUG );
+    if( !*handle )
+        return false;
+    (*handle)->p_sys = (void *) p_demux;
+    return true;
+}
+
+ts_pat_t *ts_pat_New( demux_t *p_demux )
+{
+    ts_pat_t *pat = malloc( sizeof( ts_pat_t ) );
+    if( !pat )
+        return NULL;
+
+    if( !handle_Init( p_demux, &pat->handle ) )
+    {
+        free( pat );
+        return NULL;
+    }
+
+    pat->i_version  = -1;
+    pat->i_ts_id    = -1;
+    ARRAY_INIT( pat->programs );
+
+    return pat;
+}
+
+void ts_pat_Del( demux_t *p_demux, ts_pat_t *pat )
+{
+    if( dvbpsi_decoder_present( pat->handle ) )
+        dvbpsi_pat_detach( pat->handle );
+    dvbpsi_delete( pat->handle );
+    for( int i=0; i<pat->programs.i_size; i++ )
+        PIDRelease( p_demux, pat->programs.p_elems[i] );
+    ARRAY_RESET( pat->programs );
+    free( pat );
+}
+
+ts_pmt_t *ts_pmt_New( demux_t *p_demux )
+{
+    ts_pmt_t *pmt = malloc( sizeof( ts_pmt_t ) );
+    if( !pmt )
+        return NULL;
+
+    if( !handle_Init( p_demux, &pmt->handle ) )
+    {
+        free( pmt );
+        return NULL;
+    }
+
+    ARRAY_INIT( pmt->e_streams );
+
+    pmt->i_version  = -1;
+    pmt->i_number   = -1;
+    pmt->i_pid_pcr  = 0x1FFF;
+    pmt->b_selected = false;
+    pmt->iod        = NULL;
+    pmt->od.i_version = -1;
+    ARRAY_INIT( pmt->od.objects );
+
+    pmt->i_last_dts = -1;
+
+    pmt->p_mgt      = NULL;
+
+    pmt->pcr.i_current = -1;
+    pmt->pcr.i_first  = -1;
+    pmt->pcr.b_disable = false;
+    pmt->pcr.i_first_dts = VLC_TS_INVALID;
+    pmt->pcr.i_pcroffset = -1;
+
+    pmt->pcr.b_fix_done = false;
+
+    return pmt;
+}
+
+void ts_pmt_Del( demux_t *p_demux, ts_pmt_t *pmt )
+{
+    if( dvbpsi_decoder_present( pmt->handle ) )
+        dvbpsi_pmt_detach( pmt->handle );
+    dvbpsi_delete( pmt->handle );
+    for( int i=0; i<pmt->e_streams.i_size; i++ )
+        PIDRelease( p_demux, pmt->e_streams.p_elems[i] );
+    ARRAY_RESET( pmt->e_streams );
+    if( pmt->p_mgt )
+        PIDRelease( p_demux, pmt->p_mgt );
+    if( pmt->iod )
+        ODFree( pmt->iod );
+    for( int i=0; i<pmt->od.objects.i_size; i++ )
+        ODFree( pmt->od.objects.p_elems[i] );
+    ARRAY_RESET( pmt->od.objects );
+    if( pmt->i_number > -1 )
+        es_out_Control( p_demux->out, ES_OUT_DEL_GROUP, pmt->i_number );
+    free( pmt );
+}
+
+ts_pes_es_t * ts_pes_es_New( ts_pmt_t *p_program )
+{
+    ts_pes_es_t *p_es = malloc( sizeof(*p_es) );
+    if( p_es )
+    {
+        p_es->p_program = p_program;
+        p_es->id = NULL;
+        p_es->i_sl_es_id = 0;
+        p_es->p_extraes = NULL;
+        p_es->p_next = NULL;
+        p_es->b_interlaced = false;
+        es_format_Init( &p_es->fmt, UNKNOWN_ES, 0 );
+        p_es->fmt.i_group = p_program->i_number;
+    }
+    return p_es;
+}
+
+static void ts_pes_es_Clean( demux_t *p_demux, ts_pes_es_t *p_es )
+{
+    if( p_es && p_es->id )
+    {
+        /* Ensure we don't wait for overlap hacks #14257 */
+        es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, p_es->id, false );
+        es_out_Del( p_demux->out, p_es->id );
+        p_demux->p_sys->i_pmt_es--;
+    }
+    es_format_Clean( &p_es->fmt );
+}
+
+void ts_pes_Add_es( ts_pes_t *p_pes, ts_pes_es_t *p_es, bool b_extra )
+{
+    ts_pes_es_t **pp_es = (b_extra && p_pes->p_es) ?  /* Ensure extra has main es */
+                           &p_pes->p_es->p_extraes :
+                           &p_pes->p_es;
+    if( likely(!*pp_es) )
+    {
+        *pp_es = p_es;
+    }
+    else
+    {
+        ts_pes_es_t *p_next = (*pp_es)->p_next;
+        (*pp_es)->p_next = p_es;
+        p_es->p_next = p_next;
+    }
+}
+
+ts_pes_es_t * ts_pes_Find_es( ts_pes_t *p_pes, const ts_pmt_t *p_pmt )
+{
+    for( ts_pes_es_t *p_es = p_pes->p_es; p_es; p_es = p_es->p_next )
+    {
+        if( p_es->p_program == p_pmt )
+            return p_es;
+    }
+    return NULL;
+}
+
+ts_pes_es_t * ts_pes_Extract_es( ts_pes_t *p_pes, const ts_pmt_t *p_pmt )
+{
+    ts_pes_es_t **pp_prev = &p_pes->p_es;
+    for( ts_pes_es_t *p_es = p_pes->p_es; p_es; p_es = p_es->p_next )
+    {
+        if( p_es->p_program == p_pmt )
+        {
+            *pp_prev = p_es->p_next;
+            p_es->p_next = NULL;
+            return p_es;
+        }
+        pp_prev = &p_es->p_next;
+    }
+    return NULL;
+}
+
+size_t ts_pes_Count_es( const ts_pes_es_t *p_es, bool b_active, const ts_pmt_t *p_pmt )
+{
+    size_t i=0;
+    for( ; p_es; p_es = p_es->p_next )
+    {
+        i += ( b_active ) ? !!p_es->id : ( ( !p_pmt || p_pmt == p_es->p_program ) ? 1 : 0 );
+        i += ts_pes_Count_es( p_es->p_extraes, b_active, p_pmt );
+    }
+    return i;
+}
+
+static void ts_pes_ChainDelete_es( demux_t *p_demux, ts_pes_es_t *p_es )
+{
+    while( p_es )
+    {
+        ts_pes_es_t *p_next = p_es->p_next;
+        ts_pes_ChainDelete_es( p_demux, p_es->p_extraes );
+        ts_pes_es_Clean( p_demux, p_es );
+        free( p_es );
+        p_es = p_next;
+    }
+}
+
+ts_pes_t *ts_pes_New( demux_t *p_demux, ts_pmt_t *p_program )
+{
+    VLC_UNUSED(p_demux);
+    ts_pes_t *pes = malloc( sizeof( ts_pes_t ) );
+    if( !pes )
+        return NULL;
+
+    pes->p_es = ts_pes_es_New( p_program );
+    if( !pes->p_es )
+    {
+        free( pes );
+        return NULL;
+    }
+    pes->i_stream_type = 0;
+    pes->data_type = TS_ES_DATA_PES;
+    pes->i_data_size = 0;
+    pes->i_data_gathered = 0;
+    pes->p_data = NULL;
+    pes->pp_last = &pes->p_data;
+    pes->b_always_receive = false;
+    pes->p_sections_proc = NULL;
+    pes->p_prepcr_outqueue = NULL;
+    pes->sl.p_data = NULL;
+    pes->sl.pp_last = &pes->sl.p_data;
+
+    return pes;
+}
+
+void ts_pes_Del( demux_t *p_demux, ts_pes_t *pes )
+{
+    ts_pes_ChainDelete_es( p_demux, pes->p_es );
+
+    if( pes->p_data )
+        block_ChainRelease( pes->p_data );
+
+    if( pes->p_sections_proc )
+        ts_sections_processor_ChainDelete( pes->p_sections_proc );
+
+    if( pes->p_prepcr_outqueue )
+        block_ChainRelease( pes->p_prepcr_outqueue );
+
+    free( pes );
+}
+
+ts_psi_t *ts_psi_New( demux_t *p_demux )
+{
+    ts_psi_t *psi = malloc( sizeof( ts_psi_t ) );
+    if( !psi )
+        return NULL;
+
+    if( !handle_Init( p_demux, &psi->handle ) )
+    {
+        free( psi );
+        return NULL;
+    }
+
+    psi->i_version  = -1;
+
+    return psi;
+}
+
+void ts_psi_Del( demux_t *p_demux, ts_psi_t *psi )
+{
+    VLC_UNUSED(p_demux);
+    if( dvbpsi_decoder_present( psi->handle ) )
+        dvbpsi_DetachDemux( psi->handle );
+    dvbpsi_delete( psi->handle );
+    free( psi );
+}
diff --git a/modules/demux/mpeg/ts_streams.h b/modules/demux/mpeg/ts_streams.h
new file mode 100644
index 000000000000..5a1986c910a4
--- /dev/null
+++ b/modules/demux/mpeg/ts_streams.h
@@ -0,0 +1,47 @@
+/*****************************************************************************
+ * ts_streams.h: Transport Stream input module for VLC.
+ *****************************************************************************
+ * Copyright (C) 2004-2016 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifndef VLC_TS_STREAMS_H
+#define VLC_TS_STREAMS_H
+
+typedef struct ts_pes_es_t ts_pes_es_t;
+typedef struct ts_pat_t ts_pat_t;
+typedef struct ts_pmt_t ts_pmt_t;
+typedef struct ts_pes_t ts_pes_t;
+typedef struct ts_psi_t ts_psi_t;
+
+/* Structs */
+ts_pat_t *ts_pat_New( demux_t * );
+void ts_pat_Del( demux_t *, ts_pat_t * );
+
+ts_pmt_t *ts_pmt_New( demux_t * );
+void ts_pmt_Del( demux_t *, ts_pmt_t * );
+
+ts_pes_es_t * ts_pes_es_New( ts_pmt_t * );
+void ts_pes_Add_es( ts_pes_t *, ts_pes_es_t *, bool );
+ts_pes_es_t * ts_pes_Extract_es( ts_pes_t *, const ts_pmt_t * );
+ts_pes_es_t * ts_pes_Find_es( ts_pes_t *, const ts_pmt_t * );
+size_t ts_pes_Count_es( const ts_pes_es_t *, bool, const ts_pmt_t * );
+
+ts_pes_t *ts_pes_New( demux_t *, ts_pmt_t * );
+void ts_pes_Del( demux_t *, ts_pes_t * );
+
+ts_psi_t *ts_psi_New( demux_t * );
+void ts_psi_Del( demux_t *, ts_psi_t * );
+
+#endif
diff --git a/modules/demux/mpeg/ts_streams_private.h b/modules/demux/mpeg/ts_streams_private.h
new file mode 100644
index 000000000000..b61b01250378
--- /dev/null
+++ b/modules/demux/mpeg/ts_streams_private.h
@@ -0,0 +1,128 @@
+/*****************************************************************************
+ * ts_streams_private.h: Transport Stream input module for VLC.
+ *****************************************************************************
+ * Copyright (C) 2004-2016 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************/
+#ifndef VLC_TS_STREAMS_PRIVATE_H
+#define VLC_TS_STREAMS_PRIVATE_H
+
+typedef struct dvbpsi_s dvbpsi_t;
+typedef struct ts_sections_processor_t ts_sections_processor_t;
+
+#include "mpeg4_iod.h"
+
+#include <vlc_common.h>
+#include <vlc_es.h>
+
+struct ts_pat_t
+{
+    int             i_version;
+    int             i_ts_id;
+    dvbpsi_t       *handle;
+    DECL_ARRAY(ts_pid_t *) programs;
+
+};
+
+struct ts_pmt_t
+{
+    dvbpsi_t       *handle;
+    int             i_version;
+    int             i_number;
+    int             i_pid_pcr;
+    bool            b_selected;
+    /* IOD stuff (mpeg4) */
+    od_descriptor_t *iod;
+    od_descriptors_t od;
+
+    DECL_ARRAY(ts_pid_t *) e_streams;
+
+    ts_pid_t        *p_mgt;
+
+    struct
+    {
+        mtime_t i_current;
+        mtime_t i_first; // seen <> != -1
+        /* broken PCR handling */
+        mtime_t i_first_dts;
+        mtime_t i_pcroffset;
+        bool    b_disable; /* ignore PCR field, use dts */
+        bool    b_fix_done;
+    } pcr;
+
+    mtime_t i_last_dts;
+
+};
+
+struct ts_pes_es_t
+{
+    ts_pmt_t *p_program;
+    es_format_t  fmt;
+    es_out_id_t *id;
+    uint16_t i_sl_es_id;
+    ts_pes_es_t *p_extraes; /* Some private streams encapsulate several ES (eg. DVB subtitles) */
+    ts_pes_es_t *p_next; /* Next es on same pid from different pmt (shared pid) */
+    /* J2K stuff */
+    uint8_t  b_interlaced;
+};
+
+typedef enum
+{
+    TS_ES_DATA_PES,
+    TS_ES_DATA_TABLE_SECTION
+} ts_es_data_type_t;
+
+struct ts_pes_t
+{
+    ts_pes_es_t *p_es;
+
+    uint8_t i_stream_type;
+
+    ts_es_data_type_t data_type;
+    int         i_data_size;
+    int         i_data_gathered;
+    block_t     *p_data;
+    block_t     **pp_last;
+    bool        b_always_receive;
+    ts_sections_processor_t *p_sections_proc;
+
+    block_t *   p_prepcr_outqueue;
+
+    /* SL AU */
+    struct
+    {
+        block_t     *p_data;
+        block_t     **pp_last;
+    } sl;
+};
+
+
+struct ts_psi_t
+{
+    /* for special PAT/SDT case */
+    dvbpsi_t       *handle; /* PAT/SDT/EIT */
+    int             i_version;
+
+};
+
+struct ts_psip_t
+{
+    dvbpsi_t       *handle;
+    int             i_version;
+    DECL_ARRAY(ts_pid_t *) eit;
+
+};
+
+#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a85ce89c1aee..394b72a8584a 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -494,6 +494,7 @@ modules/demux/mpeg/mpgv.c
 modules/demux/mpeg/ps.c
 modules/demux/mpeg/ps.h
 modules/demux/mpeg/ts.c
+modules/demux/mpeg/ts_psi.c
 modules/demux/nsc.c
 modules/demux/nsv.c
 modules/demux/nuv.c
-- 
GitLab