]> git.sesse.net Git - vlc/commitdiff
Added initial uncrypted blu-ray support.
authorLaurent Aimar <fenrir@videolan.org>
Fri, 23 Jan 2009 18:48:50 +0000 (19:48 +0100)
committerLaurent Aimar <fenrir@videolan.org>
Sat, 24 Jan 2009 10:27:38 +0000 (11:27 +0100)
It is very limited:
 - Uncrypted only
 - No ISO or direct disc device
 - No menu
 - No still frame
 - No angle
 - No out of mux stream (some subtitles use it)

It does:
 - title (MPLS)
 - chapter (mark)
 - correct seek
 - hide invalid ES for a given MPLS

configure.ac
modules/access/Modules.am
modules/access/bd/Modules.am [new file with mode: 0644]
modules/access/bd/bd.c [new file with mode: 0644]
modules/access/bd/clpi.c [new file with mode: 0644]
modules/access/bd/clpi.h [new file with mode: 0644]
modules/access/bd/mpls.c [new file with mode: 0644]
modules/access/bd/mpls.h [new file with mode: 0644]

index aaa767b79b45b23a9233fd882aa2b5d65a0700e5..79dfcc5b1d38018275512dac85dc005bfe310efb 100644 (file)
@@ -5821,6 +5821,7 @@ AC_CONFIG_FILES([
 
 AC_CONFIG_FILES([
   modules/access/Makefile
+  modules/access/bd/Makefile
   modules/access/bda/Makefile
   modules/access/dshow/Makefile
   modules/access/dvb/Makefile
index 971b93b5652f50e9ee8effa6fd7f15ce0be6177e..676fedcab4a20f4915a217630d73b1447b4e4b84 100644 (file)
@@ -3,7 +3,7 @@
 # obviously does not. Here is a fix for that.
 LIBTOOL=@LIBTOOL@ --tag=CC
 
-BASE_SUBDIRS = dvb mms cdda rtp rtsp rtmp vcd vcdx screen
+BASE_SUBDIRS = dvb mms cdda rtp rtsp rtmp vcd vcdx screen bd
 EXTRA_SUBDIRS = bda dshow
 SUBDIRS = $(BASE_SUBDIRS)
 DIST_SUBDIRS = $(BASE_SUBDIRS) $(EXTRA_SUBDIRS)
diff --git a/modules/access/bd/Modules.am b/modules/access/bd/Modules.am
new file mode 100644 (file)
index 0000000..3c78f06
--- /dev/null
@@ -0,0 +1,10 @@
+SOURCES_access_bd = \
+    bd.c \
+    mpls.c \
+    mpls.h \
+    clpi.c \
+    clpi.h \
+    $(NULL)
+
+libvlc_LTLIBRARIES += libaccess_bd_plugin.la
+
diff --git a/modules/access/bd/bd.c b/modules/access/bd/bd.c
new file mode 100644 (file)
index 0000000..e0c2195
--- /dev/null
@@ -0,0 +1,1321 @@
+/*****************************************************************************
+ * bd.c: BluRay Disc support (uncrypted)
+ *****************************************************************************
+ * Copyright (C) 2009 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#   include <sys/stat.h>
+#endif
+#include <limits.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_input.h>
+#include <vlc_access.h>
+#include <vlc_demux.h>
+#include <vlc_charset.h>
+#include <vlc_bits.h>
+#include <assert.h>
+
+#include "mpls.h"
+#include "clpi.h"
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+#define CACHING_TEXT N_("Caching value in ms")
+#define CACHING_LONGTEXT N_( \
+    "Caching value for BDs. This "\
+    "value should be set in milliseconds." )
+
+static int  Open ( vlc_object_t * );
+static void Close( vlc_object_t * );
+
+vlc_module_begin ()
+    set_shortname( N_("BD") )
+    set_description( N_("BD Input") )
+    set_category( CAT_INPUT )
+    set_subcategory( SUBCAT_INPUT_ACCESS )
+    add_integer( "bd-caching", DEFAULT_PTS_DELAY / 1000, NULL,
+        CACHING_TEXT, CACHING_LONGTEXT, true );
+    set_capability( "access_demux", 60 )
+    add_shortcut( "bd" )
+    set_callbacks( Open, Close )
+vlc_module_end ()
+
+/*****************************************************************************
+ * Documentation
+ * I have used:
+ *  - http://www.stebbins.biz/source/bdtools.tgz
+ *  - hdcookbook java code
+ *  - BDInfo source code
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+struct demux_sys_t
+{
+    char *psz_base;
+
+    /* */
+    int       i_mpls;
+    bd_mpls_t **pp_mpls;
+
+    /* */
+    int       i_clpi;
+    bd_clpi_t **pp_clpi;
+
+    /* */
+    int             i_title;
+    input_title_t   **pp_title;
+
+    /* */
+    es_out_t        *p_out;
+
+    /* Current state */
+    const bd_clpi_t *p_clpi;
+    int             i_clpi_ep;
+    stream_t        *p_parser;
+    stream_t        *p_m2ts;
+    int             i_play_item;
+    int             i_packet;
+    int             i_packet_start;
+    int             i_packet_stop;
+    int             i_packet_headers;
+    int64_t         i_atc_initial;
+    int64_t         i_atc_current;
+    int64_t         i_atc_wrap;
+    int64_t         i_atc_last;
+};
+
+static int Control( demux_t *, int, va_list );
+static int Demux( demux_t * );
+
+static char *FindPathBase( const char * );
+
+static int LoadPlaylist( demux_t * );
+static int LoadClip( demux_t * );
+
+static void ReorderPlaylist( demux_t * );
+
+static void InitTitles( demux_t * );
+static int  SetTitle( demux_t *, int );
+static int  SetChapter( demux_t *, int );
+static int64_t GetTime( demux_t * );
+static double  GetPosition( demux_t * );
+static int     SetTime( demux_t *, int64_t );
+static int     SetPosition( demux_t *, double );
+
+static int SetPlayItem( demux_t *p_demux, int i_mpls, int i_play_item );
+static void ClosePlayItem( demux_t * );
+
+/* */
+static int64_t GetClpiPacket( demux_t *p_demux, int *pi_ep, const bd_mpls_clpi_t *p_mpls_clpi, int64_t i_time /* in 45kHz */ );
+
+static es_out_t *EsOutNew( demux_t *p_demux );
+
+//#define BD_DEBUG
+
+/*****************************************************************************
+ * Open:
+ *****************************************************************************/
+static int Open( vlc_object_t *p_this )
+{
+    demux_t     *p_demux = (demux_t*)p_this;
+    demux_sys_t *p_sys;
+
+    if( *p_demux->psz_access && strcmp( p_demux->psz_access, "bd" ) )
+        return VLC_EGENERIC;
+
+    /* */
+    char *psz_base = FindPathBase( p_demux->psz_path );
+    if( !psz_base )
+        return VLC_EGENERIC;
+
+    msg_Dbg( p_demux, "Using path '%s'", psz_base );
+
+    /* Fill p_demux field */
+    p_demux->p_sys = p_sys = malloc( sizeof(*p_sys) );
+    if( !p_sys )
+        return VLC_EGENERIC;
+    p_sys->psz_base = psz_base;
+    TAB_INIT( p_sys->i_mpls, p_sys->pp_mpls );
+    TAB_INIT( p_sys->i_clpi, p_sys->pp_clpi );
+    TAB_INIT( p_sys->i_title, p_sys->pp_title );
+    p_demux->info.i_title = -1;
+    p_sys->p_clpi = NULL;
+    p_sys->i_clpi_ep = -1;
+    p_sys->p_parser = NULL;
+    p_sys->p_m2ts = NULL;
+    p_sys->i_play_item = -1;
+    p_sys->i_packet = -1;
+    p_sys->i_packet_start = -1;
+    p_sys->i_packet_stop = -1;
+    p_sys->i_packet_headers = -1;
+    p_sys->p_out = EsOutNew( p_demux );
+    if( !p_sys->p_out )
+        goto error;
+
+    p_demux->pf_control = Control;
+    p_demux->pf_demux = Demux;
+
+    /* Load all clip/playlist files */
+    LoadClip( p_demux );
+    LoadPlaylist( p_demux );
+
+    /* Reorder playlist to have the most significant first
+     * (as we don't have menu support, no idea how to find the main title */
+    ReorderPlaylist( p_demux );
+
+    /* Setup variables (for TS demuxer) */
+    var_Create( p_demux, "ts-es-id-pid", VLC_VAR_BOOL );
+    var_SetBool( p_demux, "ts-es-id-pid", true );
+
+    /* */
+    InitTitles( p_demux );
+    if( SetTitle( p_demux, 0 ) )
+        goto error;
+
+    return VLC_SUCCESS;
+
+error:
+    Close( VLC_OBJECT(p_demux) );
+    return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * Close:
+ *****************************************************************************/
+static void Close( vlc_object_t *p_this )
+{
+    demux_t     *p_demux = (demux_t*)p_this;
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    /* */
+    ClosePlayItem( p_demux );
+
+    /* */
+    es_out_Delete( p_sys->p_out );
+
+    /* Titles */
+    for( int i = 0; i < p_sys->i_title; i++ )
+        vlc_input_title_Delete( p_sys->pp_title[i] );
+    TAB_CLEAN( p_sys->i_title, p_sys->pp_title );
+
+    /* CLPI */
+    for( int i = 0; i < p_sys->i_clpi; i++ )
+    {
+        bd_clpi_t *p_clpi = p_sys->pp_clpi[i];
+
+        bd_clpi_Clean( p_clpi );
+        free( p_clpi );
+    }
+    TAB_CLEAN( p_sys->i_clpi, p_sys->pp_clpi );
+
+    /* MPLS */
+    for( int i = 0; i < p_sys->i_mpls; i++ )
+    {
+        bd_mpls_t *p_mpls = p_sys->pp_mpls[i];
+
+        bd_mpls_Clean( p_mpls );
+        free( p_mpls );
+    }
+    TAB_CLEAN( p_sys->i_mpls, p_sys->pp_mpls );
+
+    free( p_sys->psz_base );
+    free( p_sys );
+}
+
+/*****************************************************************************
+ * Control:
+ *****************************************************************************/
+static int Control( demux_t *p_demux, int i_query, va_list args )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    switch( i_query )
+    {
+    case DEMUX_GET_TIME:
+    {
+        int64_t *pi_time = (int64_t*)va_arg( args, int64_t * );
+        *pi_time = GetTime( p_demux );
+        return VLC_SUCCESS;;
+    }
+
+    case DEMUX_GET_POSITION:
+    {
+        double *pf_position = (double*)va_arg( args, double * );
+        *pf_position = GetPosition( p_demux );
+        return VLC_SUCCESS;
+    }
+
+    case DEMUX_SET_TIME:
+    {
+        int64_t i_time = (int64_t)va_arg( args, int64_t );
+        return SetTime( p_demux, i_time );
+    }
+    case DEMUX_SET_POSITION:
+    {
+        double f_position = (double)va_arg( args, double );
+        return SetPosition( p_demux, f_position );
+    }
+
+    case DEMUX_GET_LENGTH:
+    {
+        int64_t *pi_length = (int64_t*)va_arg( args, int64_t * );
+        *pi_length = p_sys->pp_title[p_demux->info.i_title]->i_length;
+        return VLC_SUCCESS;
+    }
+
+    /* Special for access_demux */
+    case DEMUX_CAN_PAUSE:
+    case DEMUX_CAN_SEEK:
+    case DEMUX_CAN_CONTROL_PACE:
+    {
+        bool *pb_bool = (bool*)va_arg( args, bool * );
+        *pb_bool = true;
+        return VLC_SUCCESS;
+    }
+
+    case DEMUX_SET_PAUSE_STATE:
+        return VLC_SUCCESS;
+
+    case DEMUX_GET_TITLE_INFO:
+    {
+        input_title_t ***ppp_title = (input_title_t***)va_arg( args, input_title_t*** );
+        int *pi_int    = (int*)va_arg( args, int* );
+        int *pi_title_offset = (int*)va_arg( args, int* );
+        int *pi_chapter_offset = (int*)va_arg( args, int* );
+
+        /* */
+        *pi_title_offset = 0;
+        *pi_chapter_offset = 0;
+
+        /* Duplicate title infos */
+        *pi_int = p_sys->i_title;
+        *ppp_title = calloc( p_sys->i_title, sizeof(input_title_t **) );
+        for( int i = 0; i < p_sys->i_title; i++ )
+            (*ppp_title)[i] = vlc_input_title_Duplicate( p_sys->pp_title[i] );
+
+        return VLC_SUCCESS;
+    }
+
+    case DEMUX_SET_TITLE:
+    {
+        int i_title = (int)va_arg( args, int );
+
+        if( SetTitle( p_demux, i_title ) )
+            return VLC_EGENERIC;
+        return VLC_SUCCESS;
+    }
+    case DEMUX_SET_SEEKPOINT:
+    {
+        int i_chapter = (int)va_arg( args, int );
+
+        if( SetChapter( p_demux, i_chapter ) )
+            return VLC_EGENERIC;
+        return VLC_SUCCESS;
+    }
+
+    case DEMUX_GET_PTS_DELAY:
+    {
+        int64_t *pi_delay = (int64_t*)va_arg( args, int64_t * );
+        *pi_delay = var_GetInteger( p_demux, "bd-caching" ) * INT64_C(1000);
+        return VLC_SUCCESS;
+    }
+
+    case DEMUX_GET_META:
+
+    default:
+        return VLC_EGENERIC;
+    }
+}
+
+/*****************************************************************************
+ * Demux:
+ *****************************************************************************/
+#define BD_TS_PACKET_HEADER (4)
+#define BD_TS_PACKET_SIZE (192)
+static int Demux( demux_t *p_demux )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    if( !p_sys->p_m2ts )
+        return -1;
+
+    /* */
+    if( p_sys->i_packet == p_sys->i_packet_start )
+    {
+        stream_Seek( p_sys->p_m2ts, 0 );
+
+        block_t *p_block = stream_Block( p_sys->p_m2ts,
+                                         p_sys->i_packet_headers * (int64_t)BD_TS_PACKET_SIZE + BD_TS_PACKET_HEADER );
+        if( p_block )
+        {
+            p_block->i_buffer -= BD_TS_PACKET_HEADER;
+            p_block->p_buffer += BD_TS_PACKET_HEADER;
+            stream_DemuxSend( p_sys->p_parser, p_block );
+        }
+
+        stream_Seek( p_sys->p_m2ts, p_sys->i_packet_start * (int64_t)BD_TS_PACKET_SIZE );
+    }
+
+    /* */
+    const int i_packets = __MIN( 5, p_sys->i_packet_stop - p_sys->i_packet );
+    if( i_packets <= 0 )
+    {
+        const int i_title = p_demux->info.i_title;
+        const bd_mpls_t *p_mpls = p_sys->pp_mpls[i_title];
+
+        if( p_sys->i_play_item < p_mpls->i_play_item )
+        {
+            if( !SetPlayItem( p_demux, i_title, p_sys->i_play_item + 1 ) )
+                return 1;
+            msg_Warn( p_demux, "Failed to switch to the next play item" );
+        }
+
+        /* */
+        if( SetTitle( p_demux, i_title + 1 ) )
+            return 0; /* EOF */
+        return 1;
+    }
+
+    /* XXX
+     * we ensure that the TS packet start at the begining of the buffer,
+     * it ensure proper TS parsing */
+    block_t *p_block = block_New( p_demux, i_packets * BD_TS_PACKET_SIZE + BD_TS_PACKET_HEADER );
+    if( !p_block )
+        return -1;
+
+    const int i_read = stream_Read( p_sys->p_m2ts, p_block->p_buffer, p_block->i_buffer - BD_TS_PACKET_HEADER );
+    if( i_read <= 0 )
+    {
+        msg_Err( p_demux, "Error reading current title" );
+        return -1;
+    }
+
+    if( i_read > 4 )
+    {
+        const int64_t i_atc = GetDWBE( p_block->p_buffer ) & ( (1 << 30) - 1 );
+
+        if( i_atc < p_sys->i_atc_last )
+            p_sys->i_atc_wrap += 1 << 30;
+        p_sys->i_atc_last = i_atc;
+
+        if( p_sys->i_atc_initial < 0 )
+            p_sys->i_atc_initial = i_atc + p_sys->i_atc_wrap;
+
+        p_sys->i_atc_current = i_atc + p_sys->i_atc_wrap;
+    }
+
+    p_block->i_buffer = i_read;
+    p_block->p_buffer += BD_TS_PACKET_HEADER;
+    stream_DemuxSend( p_sys->p_parser, p_block );
+
+    p_sys->i_packet += i_read / BD_TS_PACKET_SIZE;
+
+    /* Update EP */
+    if( p_sys->p_clpi->i_ep_map > 0 )
+    {
+        const int i_old_clpi_ep = p_sys->i_clpi_ep;
+
+        const bd_clpi_ep_map_t *p_ep_map = &p_sys->p_clpi->p_ep_map[0];
+        for( ; p_sys->i_clpi_ep+1 < p_ep_map->i_ep; p_sys->i_clpi_ep++ )
+        {
+            const bd_clpi_ep_t *p_ep = &p_ep_map->p_ep[p_sys->i_clpi_ep+1];
+
+            if( p_ep->i_packet > p_sys->i_packet )
+                break;
+        }
+        if( i_old_clpi_ep != p_sys->i_clpi_ep )
+        {
+            /* We have changed of EP */
+            p_sys->i_atc_initial = p_sys->i_atc_current; /* FIXME not exact */
+
+            /* Update seekpoint */
+            const input_title_t *p_title = p_sys->pp_title[p_demux->info.i_title];
+            const int64_t i_time = GetTime( p_demux );
+
+            for( ; p_demux->info.i_seekpoint+1 < p_title->i_seekpoint; p_demux->info.i_seekpoint++ )
+            {
+                const seekpoint_t *p_seekpoint = p_title->seekpoint[p_demux->info.i_seekpoint+1];
+                if( p_seekpoint->i_time_offset >  i_time )
+                    break;
+                p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
+            }
+        }
+    }
+    return 1;
+}
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+#define BD_45KHZ INT64_C(45000)
+static void InitTitles( demux_t *p_demux )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    /* */
+    for( int i = 0; i < p_sys->i_mpls; i++ )
+    {
+        const bd_mpls_t *p_mpls = p_sys->pp_mpls[i];
+
+        input_title_t *t = vlc_input_title_New();
+        if( !t )
+            break;
+
+        /* */
+        t->i_length = 0;
+        for( int j = 0; j < p_mpls->i_play_item; j++ )
+        {
+            const bd_mpls_play_item_t *p_item = &p_mpls->p_play_item[j];
+
+            t->i_length += ( p_item->i_out_time - p_item->i_in_time ) * CLOCK_FREQ / BD_45KHZ;
+        }
+
+#ifdef BD_DEBUG
+        {
+        char psz_length[MSTRTIME_MAX_SIZE];
+        msg_Warn( p_demux, "TITLE[%d] %s", i, secstotimestr( psz_length, t->i_length / CLOCK_FREQ ) );
+        }
+#endif
+
+        /* Seekpoint */
+        for( int j = 0; j < p_mpls->i_mark; j++ )
+        {
+            bd_mpls_mark_t *p_mark = &p_mpls->p_mark[j];
+
+            if( p_mark->i_type == BD_MPLS_MARK_TYPE_BOOKMARK &&
+                p_mark->i_play_item_id >= 0 && p_mark->i_play_item_id < p_mpls->i_play_item )
+            {
+                seekpoint_t *s = vlc_seekpoint_New();
+                if( !s )
+                    break;
+
+                for( int k = 0; k <= p_mark->i_play_item_id; k++ )
+                {
+                    const bd_mpls_play_item_t *p_item = &p_mpls->p_play_item[k];
+                    int64_t i_out_time;
+
+                    if( k == p_mark->i_play_item_id )
+                        i_out_time = p_mark->i_time;
+                    else
+                        i_out_time = p_item->i_out_time;
+                    s->i_time_offset += ( i_out_time - p_item->i_in_time ) * CLOCK_FREQ / BD_45KHZ;
+                }
+#ifdef BD_DEBUG
+                {
+                char psz_time[MSTRTIME_MAX_SIZE];
+                msg_Warn( p_demux, "    SEEKPOINT[%d] %s", j, secstotimestr( psz_time, s->i_time_offset / CLOCK_FREQ ) );
+                }
+#endif
+                TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
+            }
+        }
+        if( t->i_seekpoint <= 0 )
+        {
+            seekpoint_t *s = vlc_seekpoint_New();
+            if( s )
+                TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
+        }
+
+        TAB_APPEND( p_sys->i_title, p_sys->pp_title, t );
+    }
+}
+static int SetTitle( demux_t *p_demux, int i_title )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    if( i_title < 0 || i_title >= p_sys->i_title )
+        return VLC_EGENERIC;
+
+
+    if( SetPlayItem( p_demux, i_title, 0 ) )
+        return VLC_EGENERIC;
+
+    /* */
+    p_demux->info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
+    p_demux->info.i_title = i_title;
+    p_demux->info.i_seekpoint = 0;
+
+    return VLC_SUCCESS;
+}
+static int SetChapter( demux_t *p_demux, int i_chapter )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    const int i_title = p_demux->info.i_title;
+    const input_title_t *p_title = p_sys->pp_title[i_title];
+
+    if( i_chapter < 0 || i_chapter > p_title->i_seekpoint )
+        return VLC_EGENERIC;
+
+    if( SetTime( p_demux, p_title->seekpoint[i_chapter]->i_time_offset ) )
+        return VLC_EGENERIC;
+
+    return VLC_SUCCESS;
+}
+static int SetPlayItem( demux_t *p_demux, int i_mpls, int i_play_item )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    /* FIXME TODO do not reopen everything when avoidable
+     * XXX becarefull that then the es_out wrapper need some sort of
+     * locking !!! */
+
+    /* */
+    const bool b_same_mpls = i_mpls == p_demux->info.i_title;
+    const bool b_same_play_item = b_same_mpls &&
+                                  i_play_item == p_sys->i_play_item;
+
+    /* */
+    const bd_mpls_t *p_mpls = p_sys->pp_mpls[i_mpls];
+
+    /* */
+    if( i_play_item < 0 || i_play_item >= p_mpls->i_play_item )
+        return VLC_EGENERIC;
+
+    const bd_mpls_play_item_t *p_item = &p_mpls->p_play_item[i_play_item];
+    const bd_mpls_clpi_t *p_mpls_clpi = &p_item->clpi;
+
+    const bd_clpi_t *p_clpi = NULL;
+    for( int i_clpi = 0; i_clpi < p_sys->i_clpi && !p_clpi; i_clpi++ )
+    {
+        if( p_sys->pp_clpi[i_clpi]->i_id == p_mpls_clpi->i_id )
+            p_clpi = p_sys->pp_clpi[i_clpi];
+    }
+
+    const bool b_same_clpi = b_same_mpls && p_sys->p_clpi->i_id == p_clpi->i_id;
+    stream_t *p_m2ts = NULL;
+    if( !b_same_clpi )
+    {
+        char *psz_m2ts;
+        if( asprintf( &psz_m2ts, "%s/STREAM/%05d.m2ts", p_sys->psz_base, p_mpls_clpi->i_id ) < 0 )
+            return VLC_EGENERIC;
+
+        p_m2ts = stream_UrlNew( p_demux, psz_m2ts );
+        if( !p_m2ts )
+        {
+            msg_Err( p_demux, "Failed to open %s", psz_m2ts );
+            free( psz_m2ts );
+            return VLC_EGENERIC;
+        }
+        free( psz_m2ts );
+    }
+
+    /* TODO avoid reopenning the parser when unneeded.
+     * - b_same_play_item is too strict, we should check the play_items connection.
+     * - a way to completely flush the demuxer is also needed !
+     */
+    const bool b_same_parser = b_same_play_item && false;
+    stream_t *p_parser = stream_DemuxNew( p_demux, "ts", p_sys->p_out );
+    if( !p_parser )
+    {
+        msg_Err( p_demux, "Failed to create TS demuxer" );
+        if( p_m2ts )
+            stream_Delete( p_m2ts );
+        return VLC_EGENERIC;
+    }
+
+    /* */
+    if( !p_m2ts )
+    {
+        msg_Dbg( p_demux, "Reusing stream file" );
+        p_m2ts = p_sys->p_m2ts;
+        p_sys->p_m2ts = NULL;
+    }
+
+    /* */
+    ClosePlayItem( p_demux );
+
+    /* */
+    p_sys->p_clpi = p_clpi;
+    p_sys->p_parser = p_parser;
+    p_sys->p_m2ts = p_m2ts;
+    p_sys->i_play_item = i_play_item;
+
+    p_sys->i_packet_start = GetClpiPacket( p_demux, &p_sys->i_clpi_ep, p_mpls_clpi, p_item->i_in_time );
+    if( p_sys->i_packet_start < 0 )
+    {
+        p_sys->i_packet_start = 0;
+        p_sys->i_clpi_ep = 0;
+    }
+
+    p_sys->i_packet_stop = GetClpiPacket( p_demux, NULL, p_mpls_clpi, p_item->i_out_time );
+    if( p_sys->i_packet_stop < 0 )
+        p_sys->i_packet_stop = stream_Size( p_m2ts ) / BD_TS_PACKET_SIZE;
+    p_sys->i_packet = p_sys->i_packet_start;
+
+    /* This is a hack to detect the number of packet to send before any data
+     * to have the PAT/PMT. I have no idea if it is the right, but seems to work.
+     * I used a limits of 10 packets, sufficient if it is really only headers */
+    p_sys->i_packet_headers = 0;
+    if( p_clpi->i_ep_map > 0 )
+    {
+        const bd_clpi_ep_map_t *p_ep_map = &p_clpi->p_ep_map[0];
+        if( p_ep_map->i_ep > 0 )
+            p_sys->i_packet_headers = __MIN( p_ep_map->p_ep[0].i_packet, 10 );
+    }
+
+    p_sys->i_atc_initial = -1;
+    p_sys->i_atc_current = -1;
+    p_sys->i_atc_last    = -1;
+    p_sys->i_atc_wrap    = 0;
+
+    return VLC_SUCCESS;
+}
+static void ClosePlayItem( demux_t *p_demux )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    if( p_sys->p_m2ts )
+        stream_Delete( p_sys->p_m2ts );
+    if( p_sys->p_parser )
+        stream_Delete( p_sys->p_parser );
+
+    es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
+}
+
+static int64_t GetClpiPacket( demux_t *p_demux, int *pi_ep, const bd_mpls_clpi_t *p_mpls_clpi, int64_t i_time /* in 45kHz */ )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    const bd_clpi_t *p_clpi = p_sys->p_clpi;
+    assert( p_clpi );
+
+    if( p_clpi->i_ep_map <= 0 )
+        return -1;
+    const bd_clpi_ep_map_t *p_ep_map = &p_clpi->p_ep_map[0];
+
+    if( p_mpls_clpi->i_stc_id < 0 || p_mpls_clpi->i_stc_id >= p_clpi->i_stc )
+        return -1;
+
+    const bd_clpi_stc_t *p_stc = &p_clpi->p_stc[p_mpls_clpi->i_stc_id];
+#if 0
+    /* Not sure it is right */
+    if( i_time < p_stc->i_start || i_time > p_stc->i_end )
+        return -1;
+#endif
+
+    const int64_t i_packet = p_stc->i_packet;
+    int i_ep;
+    for( i_ep = 0; i_ep < p_ep_map->i_ep; i_ep++ )
+    {
+        if( p_ep_map->p_ep[i_ep].i_packet >= i_packet )
+            break;
+    }
+    if( i_ep >= p_ep_map->i_ep )
+        return -1;
+
+    for( ; i_ep < p_ep_map->i_ep; i_ep++ )
+    {
+        const bd_clpi_ep_t *p_ep = &p_ep_map->p_ep[i_ep];
+        const bd_clpi_ep_t *p_ep_next = &p_ep_map->p_ep[i_ep+1];
+
+        if( i_ep+1 < p_ep_map->i_ep && p_ep_next->i_pts / 2 > i_time )
+            break;
+        if( p_ep->i_pts / 2 >= i_time )
+            break;
+    }
+    if( i_ep >= p_ep_map->i_ep )
+        return -1;
+
+    /* */
+    if( pi_ep )
+        *pi_ep = i_ep;
+    return p_ep_map->p_ep[i_ep].i_packet;
+}
+
+/**
+ * Retreive the current time using current EP + ATC delta
+ */
+static int64_t GetTime( demux_t *p_demux )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    const int i_mpls = p_demux->info.i_title;
+
+    const bd_mpls_t *p_mpls = p_sys->pp_mpls[i_mpls];
+    const bd_mpls_play_item_t *p_item = &p_mpls->p_play_item[p_sys->i_play_item];
+
+    const bd_clpi_t *p_clpi = p_sys->p_clpi;
+    if( !p_clpi || p_clpi->i_ep_map <= 0 )
+        return 0;
+
+    /* */
+    const bd_clpi_ep_map_t *p_ep_map = &p_clpi->p_ep_map[0];
+    if( p_sys->i_clpi_ep < 0 || p_sys->i_clpi_ep >= p_ep_map->i_ep )
+        return 0;
+
+    const bd_clpi_ep_t *p_ep = &p_ep_map->p_ep[p_sys->i_clpi_ep];
+    int64_t i_time = p_ep->i_pts / 2 - p_item->i_in_time +
+                     ( p_sys->i_atc_current - p_sys->i_atc_initial ) / 300 / 2;
+
+    for( int j = 0; j < p_sys->i_play_item; j++ )
+    {
+        const bd_mpls_play_item_t *p_item = &p_mpls->p_play_item[j];
+        i_time += ( p_item->i_out_time - p_item->i_in_time );
+    }
+
+    return i_time * CLOCK_FREQ / BD_45KHZ;
+}
+
+static double GetPosition( demux_t *p_demux )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    const int64_t i_time = GetTime( p_demux );
+    const input_title_t *p_title = p_sys->pp_title[p_demux->info.i_title];
+
+    if( p_title->i_length <= 0 )
+        return 0.0;
+
+    return (double)i_time / p_title->i_length;
+}
+
+static int SetTime( demux_t *p_demux, int64_t i_time )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    const int i_mpls = p_demux->info.i_title;
+    const input_title_t *p_title = p_sys->pp_title[i_mpls];
+    const bd_mpls_t *p_mpls = p_sys->pp_mpls[i_mpls];
+
+    /* Find the play item */
+    int i_item;
+    int64_t i_play_item_time = 0;
+    for( i_item = 0; i_item < p_mpls->i_play_item; i_item++ )
+    {
+        const bd_mpls_play_item_t *p_item = &p_mpls->p_play_item[i_item];
+        const int64_t i_duration = ( p_item->i_out_time - p_item->i_in_time ) * CLOCK_FREQ / BD_45KHZ;
+
+        if( i_time >= i_play_item_time && i_time < i_play_item_time + i_duration )
+            break;
+
+        i_play_item_time += i_duration;
+    }
+
+    if( i_item >= p_mpls->i_play_item )
+        return VLC_EGENERIC;
+
+    if( SetPlayItem( p_demux, i_mpls, i_item ) )
+        return VLC_EGENERIC;
+
+
+    /* Find the right entry point */
+    if( p_sys->p_clpi->i_ep_map <= 0 )
+        goto update;
+
+    const bd_clpi_ep_map_t *p_ep_map = &p_sys->p_clpi->p_ep_map[0];
+    if( p_ep_map->i_ep <= 0 )
+        goto update;
+
+    int64_t i_next_display_date = -1;
+    for( ; p_sys->i_clpi_ep+1 < p_ep_map->i_ep; p_sys->i_clpi_ep++ )
+    {
+        const bd_clpi_ep_t *p_next = &p_ep_map->p_ep[p_sys->i_clpi_ep+1];
+        const int64_t i_next_time = i_play_item_time + ( ( p_next->i_pts / 2 - p_mpls->p_play_item[i_item].i_in_time ) * CLOCK_FREQ / BD_45KHZ );
+
+        if( i_next_time > i_time )
+        {
+            const bd_clpi_ep_t *p_ep = &p_ep_map->p_ep[p_sys->i_clpi_ep];
+            const int64_t i_ep_time = i_play_item_time + ( ( p_ep->i_pts / 2 - p_mpls->p_play_item[i_item].i_in_time ) * CLOCK_FREQ / BD_45KHZ );
+
+
+            i_next_display_date = p_ep->i_pts * CLOCK_FREQ / 90000 + ( i_time - i_ep_time );
+            break;
+        }
+    }
+
+    const bd_clpi_ep_t *p_ep = &p_ep_map->p_ep[p_sys->i_clpi_ep];
+    p_sys->i_packet_start =
+    p_sys->i_packet       = p_ep->i_packet;
+
+    if( i_next_display_date >= 0 )
+        es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, i_next_display_date );
+
+update:
+    /* Update seekpoint */
+    for( p_demux->info.i_seekpoint = 0; p_demux->info.i_seekpoint+1 < p_title->i_seekpoint; p_demux->info.i_seekpoint++ )
+    {
+        const seekpoint_t *p_seekpoint = p_title->seekpoint[p_demux->info.i_seekpoint+1];
+        if( p_seekpoint->i_time_offset >  i_time )
+            break;
+    }
+    p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
+    return VLC_SUCCESS;
+}
+
+static int SetPosition( demux_t *p_demux, double f_position )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    const input_title_t *p_title = p_sys->pp_title[p_demux->info.i_title];
+
+    if( p_title->i_length <= 0 )
+        return VLC_EGENERIC;
+
+    return SetTime( p_demux, f_position * p_title->i_length );
+}
+
+/*****************************************************************************
+ * Mpls ordering
+ *****************************************************************************/
+static int64_t GetMplsUniqueDuration( const bd_mpls_t *p_mpls )
+{
+    int64_t i_length = 0;
+
+    for( int i = 0; i < p_mpls->i_play_item; i++ )
+    {
+        const bd_mpls_play_item_t *p_item0 = &p_mpls->p_play_item[i];
+        int j;
+        for( j = i+1; j < p_mpls->i_play_item; j++ )
+        {
+            const bd_mpls_play_item_t *p_item1 = &p_mpls->p_play_item[j];
+            if( p_item0->clpi.i_id == p_item1->clpi.i_id &&
+                p_item0->clpi.i_stc_id == p_item1->clpi.i_stc_id &&
+                p_item0->i_in_time == p_item1->i_in_time &&
+                p_item0->i_out_time == p_item1->i_out_time )
+                break;
+        }
+        if( j >= p_mpls->i_play_item )
+            i_length += p_item0->i_out_time - p_item0->i_in_time;
+    }
+    return i_length;
+}
+static int SortMpls( void **pp_a, void **pp_b )
+{
+    const int64_t i_length_a = GetMplsUniqueDuration( *pp_a );
+    const int64_t i_length_b = GetMplsUniqueDuration( *pp_b );
+
+    if( i_length_a == i_length_b )
+        return 0;
+    return i_length_a < i_length_b ? 1 : -1;
+}
+
+static void ReorderPlaylist( demux_t *p_demux )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    qsort( p_sys->pp_mpls, p_sys->i_mpls, sizeof(*p_sys->pp_mpls), SortMpls );
+}
+/*****************************************************************************
+ * Helpers:
+ *****************************************************************************/
+/* */
+static char *FindPathBase( const char *psz_path )
+{
+    struct stat s;
+    char *psz_tmp;
+
+    /* */
+    char *psz_base = strdup( psz_path );
+    if( !psz_base )
+        return NULL;
+
+    /* */
+    while( *psz_base && psz_base[strlen(psz_base)-1] == '/' )
+        psz_base[strlen(psz_base)-1] = '\0';
+
+    /* */
+    if( utf8_stat( psz_base, &s ) || !S_ISDIR( s.st_mode ) )
+        goto error;
+
+    /* Check BDMV */
+    if( asprintf( &psz_tmp, "%s/BDMV", psz_base ) < 0 )
+        goto error;
+    if( !utf8_stat( psz_tmp, &s ) && S_ISDIR( s.st_mode ) )
+    {
+        free( psz_base );
+        psz_base = psz_tmp;
+    }
+    else
+    {
+        free( psz_tmp );
+    }
+
+    /* Check presence of mandatory files */
+    for( int i = 0;; i++ )
+    {
+        static const char *ppsz_name[] = {
+            "index.bdmv",
+            "MovieObject.bdmv",
+            NULL
+        };
+        if( !ppsz_name[i] )
+            break;
+
+        if( asprintf( &psz_tmp, "%s/%s", psz_base, ppsz_name[i] ) < 0 )
+            goto error;
+
+        bool b_ok = utf8_stat( psz_tmp, &s ) == 0 && S_ISREG( s.st_mode );
+
+        free( psz_tmp );
+        if( !b_ok )
+            goto error;
+    }
+
+    return psz_base;
+
+error:
+    free( psz_base );
+    return NULL;
+}
+
+/* */
+static block_t *LoadBlock( demux_t *p_demux, const char *psz_name )
+{
+    stream_t *s = stream_UrlNew( p_demux, psz_name );
+    if( !s )
+        return NULL;
+
+    const int64_t i_size = stream_Size( s );
+    block_t *p_block = NULL;
+
+    if( i_size > 0 && i_size < INT_MAX )
+        p_block = stream_Block( s, i_size );
+
+    stream_Delete( s );
+
+    return p_block;
+}
+
+/* */
+static int FilterMpls( const char *psz_name )
+{
+    return strlen( psz_name ) == strlen( "xxxxx.mpls" ) &&
+           !strcmp( &psz_name[5], ".mpls" );
+}
+
+static void LoadMpls( demux_t *p_demux, const char *psz_name, int i_id )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+#if defined(BD_DEBUG)
+    msg_Err( p_demux, "Loading %s", psz_name );
+#endif
+
+    block_t *p_block = LoadBlock( p_demux, psz_name );
+    if( !p_block )
+        goto error;
+
+    /* */
+    bd_mpls_t *p_mpls = malloc( sizeof(*p_mpls) );
+    if( !p_mpls )
+        goto error;
+
+    /* */
+    bs_t s;
+    bs_init( &s, p_block->p_buffer, p_block->i_buffer );
+
+    if( bd_mpls_Parse( p_mpls, &s, i_id ) )
+        goto error;
+
+#if defined(BD_DEBUG)
+    msg_Err( p_demux, "MPLS: id=%d", p_mpls->i_id );
+    msg_Err( p_demux, "MPLS: play_item=%d sub_path=%d",
+             p_mpls->i_play_item, p_mpls->i_sub_path );
+
+    for( int i = 0; i < p_mpls->i_play_item; i++ )
+    {
+        bd_mpls_play_item_t *p_item = &p_mpls->p_play_item[i];
+
+        msg_Err( p_demux, "PLAY_ITEM[%d] connection=%d in=%d out=%d still=%d(%d)",
+                 i, p_item->i_connection,
+                 (int)p_item->i_in_time, (int)p_item->i_out_time,
+                 p_item->i_still, p_item->i_still_time );
+        msg_Err( p_demux, "     clpi_default: id=%d stc_id=%d",
+                 p_item->clpi.i_id, p_item->clpi.i_stc_id );
+        for( int j = 0; j < p_item->i_clpi; j++ )
+            msg_Err( p_demux, "     clpi[%d]: id=%d stc_id=%d",
+                     j, p_item->p_clpi[j].i_id, p_item->p_clpi[j].i_stc_id );
+        for( int j = 0; j < p_item->i_stream; j++ )
+            msg_Err( p_demux, "     stream[%d]: type=%d class=%d stream_type=0x%x lang=%s charset=%d",
+                     j,
+                     p_item->p_stream[j].i_type,
+                     p_item->p_stream[j].i_class,
+                     p_item->p_stream[j].i_stream_type,
+                     p_item->p_stream[j].psz_language,
+                     p_item->p_stream[j].i_charset );
+    }
+
+    for( int i = 0; i < p_mpls->i_sub_path; i++ )
+    {
+        bd_mpls_sub_path_t *p_sub = &p_mpls->p_sub_path[i];
+
+        msg_Err( p_demux, "SUB_PATH[%d] type=%d repeat=%d item=%d",
+                 i, p_sub->i_type, p_sub->b_repeat, p_sub->i_item );
+    }
+
+    for( int i = 0; i < p_mpls->i_mark; i++ )
+    {
+        bd_mpls_mark_t *p_mark = &p_mpls->p_mark[i];
+
+        msg_Err( p_demux, "M[%d] t=%d play_item_id=%d time=%d entry_es_pid=%d",
+                 i, p_mark->i_type, p_mark->i_play_item_id, (int)p_mark->i_time, p_mark->i_entry_es_pid );
+    }
+#endif
+
+    /* */
+    TAB_APPEND( p_sys->i_mpls, p_sys->pp_mpls, p_mpls );
+
+    /* */
+    block_Release( p_block );
+    return;
+
+error:
+    msg_Err( p_demux, "Failed loading %s", psz_name );
+    if( p_block )
+        block_Release( p_block );
+}
+
+/* */
+static int FilterClpi( const char *psz_name )
+{
+    return strlen( psz_name ) == strlen( "xxxxx.clpi" ) &&
+           !strcmp( &psz_name[5], ".clpi" );
+}
+
+static void LoadClpi( demux_t *p_demux, const char *psz_name, int i_id )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+#if defined(BD_DEBUG)
+    msg_Err( p_demux, "Loading %s", psz_name );
+#endif
+
+    block_t *p_block = LoadBlock( p_demux, psz_name );
+    if( !p_block )
+        goto error;
+
+    /* */
+    bd_clpi_t *p_clpi = malloc( sizeof(*p_clpi) );
+    if( !p_clpi )
+        goto error;
+
+    /* */
+    bs_t s;
+    bs_init( &s, p_block->p_buffer, p_block->i_buffer );
+
+    if( bd_clpi_Parse( p_clpi, &s, i_id ) )
+        goto error;
+
+#if defined(BD_DEBUG)
+    msg_Err( p_demux, "CLPI: id=%d", p_clpi->i_id );
+    msg_Err( p_demux, "CLPI: STC=%d", p_clpi->i_stc );
+    for( int i = 0; i < p_clpi->i_stc; i++ )
+        msg_Err( p_demux, "   STC[%d] pcr_pid=%d packet=%d start=%d end=%d",
+                 i, p_clpi->p_stc[i].i_pcr_pid, (int)p_clpi->p_stc[i].i_packet,
+                 (int)p_clpi->p_stc[i].i_start, (int)p_clpi->p_stc[i].i_end );
+    msg_Err( p_demux, "CLPI: Stream=%d", p_clpi->i_stream );
+    for( int i = 0; i < p_clpi->i_stream; i++ )
+        msg_Err( p_demux, "   Stream[%d] pid=%d type=0x%x",
+                 i, p_clpi->p_stream[i].i_pid, p_clpi->p_stream[i].i_type );
+    msg_Err( p_demux, "CLPI: Ep Map=%d", p_clpi->i_ep_map );
+    for( int i = 0; i < p_clpi->i_ep_map; i++ )
+    {
+        const bd_clpi_ep_map_t *p_ep_map = &p_clpi->p_ep_map[i];
+        msg_Err( p_demux, "   Ep Map[%d] pid=%d type=0x%x entry_point=%d",
+                 i, p_ep_map->i_pid, p_ep_map->i_type, p_ep_map->i_ep );
+        for( int j = 0; j < p_ep_map->i_ep; j++ )
+        {
+            msg_Err( p_demux, "      Ep[%d] packet=%d pts=%d",
+                     j, (int)p_ep_map->p_ep[j].i_packet, (int)p_ep_map->p_ep[j].i_pts );
+        }
+    }
+#endif
+
+    /* */
+    TAB_APPEND( p_sys->i_clpi, p_sys->pp_clpi, p_clpi );
+
+    /* */
+    block_Release( p_block );
+    return;
+
+error:
+    msg_Err( p_demux, "Failed loading %s", psz_name );
+    if( p_block )
+        block_Release( p_block );
+}
+
+/* */
+static int ScanSort( const char **ppsz_a, const char **ppsz_b )
+{
+    return strcmp( *ppsz_a, *ppsz_b );
+}
+
+static int Load( demux_t *p_demux,
+                 const char *psz_dir,
+                 int (*pf_filter)( const char * ),
+                 void (*pf_load)( demux_t *p_demux, const char *psz_name, int i_id ) )
+{
+    char *psz_playlist;
+    if( asprintf( &psz_playlist, "%s/%s", p_demux->p_sys->psz_base, psz_dir ) < 0 )
+        return VLC_EGENERIC;
+
+    char **ppsz_list;
+
+    int i_list = utf8_scandir( psz_playlist, &ppsz_list, pf_filter, ScanSort );
+
+    for( int i = 0; i < i_list; i++ )
+    {
+        char *psz_file = ppsz_list[i];
+        if( !psz_file )
+            break;
+
+        char *psz_name;
+        if( asprintf( &psz_name, "%s/%s/%s", p_demux->p_sys->psz_base, psz_dir, psz_file ) >= 0)
+        {
+            const int i_id = strtol( psz_file, NULL, 10 );
+            pf_load( p_demux, psz_name, i_id );
+            free( psz_name );
+        }
+        free( psz_file );
+    }
+    free( ppsz_list );
+
+    free( psz_playlist );
+    return VLC_SUCCESS;
+}
+
+static int LoadPlaylist( demux_t *p_demux )
+{
+    return Load( p_demux, "PLAYLIST", FilterMpls, LoadMpls );
+}
+static int LoadClip( demux_t *p_demux )
+{
+    return Load( p_demux, "CLIPINF", FilterClpi, LoadClpi );
+}
+
+/* */
+struct es_out_sys_t
+{
+    demux_t *p_demux;
+};
+
+static es_out_id_t *EsOutAdd( es_out_t *p_out, const es_format_t *p_fmt )
+{
+    demux_t *p_demux = p_out->p_sys->p_demux;
+    const bd_mpls_t *p_mpls = p_demux->p_sys->pp_mpls[p_demux->info.i_title];
+    const bd_mpls_play_item_t *p_item = &p_mpls->p_play_item[p_demux->p_sys->i_play_item];
+
+    es_format_t fmt;
+
+    es_format_Copy( &fmt, p_fmt );
+    fmt.i_priority = -2;
+
+    for( int i = 0; i < p_item->i_stream; i++ )
+    {
+        const bd_mpls_stream_t *p_stream = &p_item->p_stream[i];
+        if( p_stream->i_type != BD_MPLS_STREAM_TYPE_PLAY_ITEM ||
+            p_stream->play_item.i_pid != fmt.i_id )
+            continue;
+
+        /* TODO improved priority for higher quality stream ?
+         * if so, extending stream attributes parsing might be a good idea
+         */
+        fmt.i_priority = 0;
+
+#if 0
+        /* Useless, and beside not sure it is the right thing to do */
+        free( fmt.psz_description );
+        switch( p_stream->i_class )
+        {
+        case BD_MPLS_STREAM_CLASS_SECONDARY_AUDIO:
+            fmt.psz_description = strdup( "Secondary audio" );
+            break;
+        default:
+            fmt.psz_description = NULL;
+            break;
+        }
+#endif
+
+        //msg_Err( p_demux, "Found ref for stream pid %d", fmt.i_id );
+        if( *p_stream->psz_language && ( !fmt.psz_language || *fmt.psz_language == '\0' ) )
+        {
+            free( fmt.psz_language );
+            fmt.psz_language = strdup( p_stream->psz_language );
+        }
+        switch( p_stream->i_charset )
+        {
+        /* TODO add all values */
+        default:
+            break;
+        }
+        break;
+    }
+    if( fmt.i_priority < 0 )
+        msg_Dbg( p_demux, "Hidding one stream (pid=%d)", fmt.i_id );
+
+    /* */
+    es_out_id_t *p_es = es_out_Add( p_demux->out, &fmt );
+
+    es_format_Clean( &fmt );
+    return p_es;
+}
+static int EsOutSend( es_out_t *p_out, es_out_id_t *p_es, block_t *p_block )
+{
+    return es_out_Send( p_out->p_sys->p_demux->out, p_es, p_block );
+}
+static void EsOutDel( es_out_t *p_out, es_out_id_t *p_es )
+{
+    es_out_Del( p_out->p_sys->p_demux->out, p_es );
+}
+static int EsOutControl( es_out_t *p_out, int i_query, va_list args )
+{
+    return es_out_vaControl( p_out->p_sys->p_demux->out, i_query, args );
+}
+static void EsOutDestroy( es_out_t *p_out )
+{
+    free( p_out->p_sys );
+    free( p_out );
+}
+
+static es_out_t *EsOutNew( demux_t *p_demux )
+{
+    es_out_t *p_out = malloc( sizeof(*p_out) );
+    es_out_sys_t *p_sys;
+
+    if( !p_out )
+        return NULL;
+
+    p_out->pf_add     = EsOutAdd;
+    p_out->pf_send    = EsOutSend;
+    p_out->pf_del     = EsOutDel;
+    p_out->pf_control = EsOutControl;
+    p_out->pf_destroy = EsOutDestroy;
+    p_out->b_sout = false;
+
+    p_out->p_sys = p_sys = malloc( sizeof(*p_sys) );
+    if( !p_sys )
+    {
+        free( p_out );
+        return NULL;
+    }
+    p_sys->p_demux = p_demux;
+
+    return p_out;
+}
+
diff --git a/modules/access/bd/clpi.c b/modules/access/bd/clpi.c
new file mode 100644 (file)
index 0000000..cba1296
--- /dev/null
@@ -0,0 +1,209 @@
+/*****************************************************************************
+ * clpi.c: BluRay Disc CLPI
+ *****************************************************************************
+ * Copyright (C) 2009 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <limits.h>
+
+#include <vlc_common.h>
+#include <vlc_bits.h>
+#include "clpi.h"
+
+/* */
+void bd_clpi_stc_Parse( bd_clpi_stc_t *p_stc, bs_t *s )
+{
+    p_stc->i_pcr_pid = bs_read( s, 16 );
+    p_stc->i_packet = bs_read( s, 32 );
+    p_stc->i_start = bs_read( s, 32 );
+    p_stc->i_end = bs_read( s, 32 );
+}
+
+void bd_clpi_stream_Parse( bd_clpi_stream_t *p_stream, bs_t *s )
+{
+    p_stream->i_pid = bs_read( s, 16 );
+
+    const int i_length = bs_read( s, 8 );
+
+    p_stream->i_type = bs_read( s, 8 );
+
+    /* Ignore the rest */
+    if( i_length > 1 )
+        bs_skip( s, 8*i_length - 8 );
+}
+
+void bd_clpi_ep_map_Clean( bd_clpi_ep_map_t *p_ep_map )
+{
+    free( p_ep_map->p_ep );
+}
+int bd_clpi_ep_map_Parse( bd_clpi_ep_map_t *p_ep_map,
+                          bs_t *s, const int i_ep_map_start )
+{
+    p_ep_map->i_pid = bs_read( s, 16 );
+    bs_skip( s, 10 );
+    p_ep_map->i_type = bs_read( s, 4 );
+
+    const int i_coarse = bs_read( s, 16 );
+    const int i_fine = bs_read( s, 18 );
+    const uint32_t i_coarse_start = bs_read( s, 32 );
+
+    p_ep_map->i_ep = i_fine;
+    p_ep_map->p_ep = calloc( i_fine, sizeof(*p_ep_map->p_ep) );
+    if( !p_ep_map->p_ep )
+        return VLC_EGENERIC;
+
+    bs_t cs = *s;
+    bs_skip( &cs, 8*(i_ep_map_start + i_coarse_start) - bs_pos( s ) );
+
+    const uint32_t i_fine_start = bs_read( &cs, 32 );
+
+    for( int i = 0; i < i_coarse; i++ )
+    {
+        const int      i_fine_id = bs_read( &cs, 18 );
+        const int      i_pts = bs_read( &cs, 14 );
+        const uint32_t i_packet = bs_read( &cs, 32 );
+
+        for( int j = i_fine_id; j < p_ep_map->i_ep; j++ )
+        {
+            p_ep_map->p_ep[j].i_pts = (int64_t)(i_pts & ~1) << 19;
+            p_ep_map->p_ep[j].i_packet = i_packet & ~( (1 << 17) - 1 );
+        }
+    }
+
+    bs_t fs = *s;
+    bs_skip( &fs, 8*(i_ep_map_start + i_coarse_start + i_fine_start) - bs_pos( s ) );
+    for( int i = 0; i < i_fine; i++ )
+    {
+        const bool b_angle_point = bs_read( &fs, 1 );
+        bs_skip( &fs, 3 );  /* I end position offset */
+        const int i_pts = bs_read( &fs, 11 );
+        const int i_packet = bs_read( &fs, 17 );
+
+        p_ep_map->p_ep[i].b_angle_point = b_angle_point;
+        p_ep_map->p_ep[i].i_pts |= i_pts << 9;
+        p_ep_map->p_ep[i].i_packet |= i_packet;
+    }
+    return VLC_SUCCESS;
+}
+
+void bd_clpi_Clean( bd_clpi_t *p_clpi )
+{
+    free( p_clpi->p_stc );
+
+    free( p_clpi->p_stream );
+
+    for( int i = 0; i < p_clpi->i_ep_map; i++ )
+        bd_clpi_ep_map_Clean( &p_clpi->p_ep_map[i] );
+    free( p_clpi->p_ep_map );
+}
+
+int bd_clpi_Parse( bd_clpi_t *p_clpi, bs_t *s, int i_id )
+{
+    const int i_start = bs_pos( s ) / 8;
+
+    /* */
+    if( bs_read( s, 32 ) != 0x48444D56 )
+        return VLC_EGENERIC;
+    if( bs_read( s, 32 ) != 0x30323030 )
+        return VLC_EGENERIC;
+
+    /* */
+    const uint32_t i_sequence_start = bs_read( s, 32 );
+    const uint32_t i_program_start = bs_read( s, 32 );
+    const uint32_t i_cpi_start = bs_read( s, 32 );
+    bs_skip( s, 32 );   /* mark start */
+    bs_skip( s, 32 );   /* extension start */
+
+    /* */
+    p_clpi->i_id = i_id;
+
+    /* Read sequence */
+    bs_t ss = *s;
+    bs_skip( &ss, 8 * ( i_start + i_sequence_start ) - bs_pos( s ) );
+    bs_skip( &ss, 32 ); /* Length */
+    bs_skip( &ss, 8 );
+    bs_skip( &ss, 8 );  /* ATC sequence count (MUST be 1 ?) */
+    bs_skip( &ss, 32 ); /* ATC start (MUST be 0) */
+    const int i_stc = bs_read( &ss, 8 );
+    bs_skip( &ss, 8 );  /* STC ID offset (MUST be 0 ? */
+
+    p_clpi->p_stc = calloc( i_stc, sizeof(*p_clpi->p_stc) );
+    for( p_clpi->i_stc = 0; p_clpi->i_stc < i_stc; p_clpi->i_stc++ )
+    {
+        if( !p_clpi->p_stc )
+            break;
+        bd_clpi_stc_Parse( &p_clpi->p_stc[p_clpi->i_stc], &ss );
+    }
+
+    /* Program */
+    bs_t ps = *s;
+    bs_skip( &ps, 8 * ( i_start + i_program_start ) - bs_pos( s ) );
+    bs_skip( &ps, 32 ); /* Length */
+    bs_skip( &ps, 8 );
+    bs_skip( &ps, 8 );  /* Program count (MUST be 1 ?) */
+    bs_skip( &ps, 32 ); /* Program sequence start (MUST be 0) */
+    p_clpi->i_pmt_pid = bs_read( &ps, 16 );
+    const int i_stream = bs_read( &ps, 8 );
+    bs_skip( &ps, 8 );  /* Group count (MUST be 1 ?) */
+
+    p_clpi->p_stream = calloc( i_stream, sizeof(*p_clpi->p_stream) );
+    for( p_clpi->i_stream = 0; p_clpi->i_stream < i_stream; p_clpi->i_stream++ )
+    {
+        if( !p_clpi->p_stream )
+            break;
+        bd_clpi_stream_Parse( &p_clpi->p_stream[p_clpi->i_stream], &ps );
+    }
+
+    /* Read CPI */
+    bs_t cs = *s;
+    bs_skip( &cs, 8 * ( i_start + i_cpi_start ) - bs_pos( s ) );
+
+    const uint32_t i_cpi_length = bs_read( &cs, 32 );
+    if( i_cpi_length > 0 )
+    {
+        bs_skip( &cs, 12 );
+        bs_skip( &cs, 4 );  /* Type (MUST be 1) */
+
+        /* EPMap */
+        const int i_epmap_start = bs_pos( &cs ) / 8;
+        bs_skip( &cs, 8 );
+        const int i_ep_map = bs_read( &cs, 8 );
+
+        p_clpi->p_ep_map = calloc( i_ep_map, sizeof(*p_clpi->p_ep_map) );
+        for( p_clpi->i_ep_map = 0; p_clpi->i_ep_map < i_ep_map; p_clpi->i_ep_map++ )
+        {
+            if( !p_clpi->p_ep_map )
+                break;
+
+            if( bd_clpi_ep_map_Parse( &p_clpi->p_ep_map[p_clpi->i_ep_map],
+                                      &cs, i_epmap_start ) )
+                break;
+        }
+    }
+    else
+    {
+        p_clpi->i_ep_map = 0;
+        p_clpi->p_ep_map = NULL;
+    }
+    return VLC_SUCCESS;
+}
diff --git a/modules/access/bd/clpi.h b/modules/access/bd/clpi.h
new file mode 100644 (file)
index 0000000..c2385f7
--- /dev/null
@@ -0,0 +1,75 @@
+/*****************************************************************************
+ * clpi.h: BluRay Disc CLPI
+ *****************************************************************************
+ * Copyright (C) 2009 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+typedef struct
+{
+    int     i_pcr_pid;
+    int64_t i_packet;   /* TS Packet number */
+    int64_t i_start;    /* Presentation start time in 45kHz */
+    int64_t i_end;      /* Presentation end time in 45kHz */
+} bd_clpi_stc_t;
+void bd_clpi_stc_Parse( bd_clpi_stc_t *p_stc, bs_t *s );
+
+typedef struct
+{
+    int i_pid;          /* PID of the associated stream */
+    int i_type;         /* Stream type of the associated stream */
+} bd_clpi_stream_t;
+void bd_clpi_stream_Parse( bd_clpi_stream_t *p_stream, bs_t *s );
+
+typedef struct
+{
+    bool    b_angle_point;  /* EP angle point change */
+    int64_t i_packet;       /* TS packet number */
+    int64_t i_pts;          /* PTS of the associated stream (90kHz, 33bits) */
+} bd_clpi_ep_t;
+
+typedef struct
+{
+    int i_pid;          /* PID of the associated stream */
+    int i_type;         /* Stream type of the associated stream */
+
+    int          i_ep;
+    bd_clpi_ep_t *p_ep;
+} bd_clpi_ep_map_t;
+void bd_clpi_ep_map_Clean( bd_clpi_ep_map_t *p_ep_map );
+int bd_clpi_ep_map_Parse( bd_clpi_ep_map_t *p_ep_map,
+                          bs_t *s, const int i_ep_map_start );
+
+typedef struct
+{
+    int              i_id;
+
+    int              i_stc;
+    bd_clpi_stc_t    *p_stc;
+    
+    int              i_pmt_pid;
+    int              i_stream;
+    bd_clpi_stream_t *p_stream;
+
+    int              i_ep_map;
+    bd_clpi_ep_map_t *p_ep_map;
+} bd_clpi_t;
+void bd_clpi_Clean( bd_clpi_t *p_clpi );
+int bd_clpi_Parse( bd_clpi_t *p_clpi, bs_t *s, int i_id );
+
diff --git a/modules/access/bd/mpls.c b/modules/access/bd/mpls.c
new file mode 100644 (file)
index 0000000..1b372ae
--- /dev/null
@@ -0,0 +1,350 @@
+/*****************************************************************************
+ * mpls.c: BluRay Disc MPLS
+ *****************************************************************************
+ * Copyright (C) 2009 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <limits.h>
+
+#include <vlc_common.h>
+#include <vlc_bits.h>
+#include "mpls.h"
+
+
+/* MPLS */
+void bd_mpls_stream_Parse( bd_mpls_stream_t *p_stream, bs_t *s, int i_class )
+{
+    /* Stream entry parsing */
+    const int i_entry_length = bs_read( s, 8 );
+    const int i_entry_start = bs_pos( s ) / 8;
+
+    p_stream->i_type = bs_read( s, 8 );
+    p_stream->i_class = i_class;
+    if( p_stream->i_type == BD_MPLS_STREAM_TYPE_PLAY_ITEM )
+    {
+        p_stream->play_item.i_pid = bs_read( s, 16 );
+    }
+    else if( p_stream->i_type == BD_MPLS_STREAM_TYPE_SUB_PATH )
+    {
+        p_stream->sub_path.i_sub_path_id = bs_read( s, 8 );
+        p_stream->sub_path.i_sub_clip_id = bs_read( s, 8 );
+        p_stream->sub_path.i_pid = bs_read( s, 16 );
+    }
+    else if( p_stream->i_type == BD_MPLS_STREAM_TYPE_IN_MUX_SUB_PATH )
+    {
+        p_stream->in_mux_sub_path.i_sub_path_id = bs_read( s, 8 );
+        p_stream->in_mux_sub_path.i_pid = bs_read( s, 16 );
+    }
+    bs_skip( s, 8 * ( i_entry_start + i_entry_length ) - bs_pos( s ) );
+
+    /* Stream attributes parsing */
+    const int i_attributes_length = bs_read( s, 8 );
+    const int i_attributes_start = bs_pos( s ) / 8;
+
+    p_stream->i_stream_type = bs_read( s, 8 );
+    strcpy( p_stream->psz_language, "" );
+    p_stream->i_charset = -1;
+
+    if( p_stream->i_stream_type == 0x02 || /* MPEG-I/II */
+        p_stream->i_stream_type == 0x1b || /* AVC */
+        p_stream->i_stream_type == 0xea )  /* VC-1 */
+    {
+        /* Video */
+    }
+    else if( ( p_stream->i_stream_type >= 0x80 && p_stream->i_stream_type <= 0x8f ) ||
+             ( p_stream->i_stream_type >= 0xa0 && p_stream->i_stream_type <= 0xaf ) )
+    {
+        /* Audio */
+        bs_skip( s, 4 );
+        bs_skip( s, 4 );
+        for( int i = 0; i < 3; i++ )
+            p_stream->psz_language[i] = bs_read( s, 8 );
+        p_stream->psz_language[3] = '\0';
+    }
+    else if( p_stream->i_stream_type == 0x90 ||   /* PG stream */
+             p_stream->i_stream_type == 0x91 )    /* IG stream */
+    {
+        for( int i = 0; i < 3; i++ )
+            p_stream->psz_language[i] = bs_read( s, 8 );
+        p_stream->psz_language[3] = '\0';
+    }
+    else if( p_stream->i_stream_type == 0x92 )    /* Text stream */
+    {
+        p_stream->i_charset = bs_read( s, 8 );
+        for( int i = 0; i < 3; i++ )
+            p_stream->psz_language[i] = bs_read( s, 8 );
+        p_stream->psz_language[3] = '\0';
+    }
+
+    bs_skip( s, 8 * ( i_attributes_start + i_attributes_length ) - bs_pos( s ) );
+}
+
+void bd_mpls_play_item_Clean( bd_mpls_play_item_t *p_item )
+{
+    free( p_item->p_clpi );
+    free( p_item->p_stream );
+}
+void bd_mpls_play_item_Parse( bd_mpls_play_item_t *p_item, bs_t *s )
+{
+    const int i_length = bs_read( s, 16 );
+    const int i_start = bs_pos( s ) / 8;
+
+    char psz_name[5+1];
+    for( int j = 0; j < 5; j++ )
+        psz_name[j] = bs_read( s, 8 );
+    psz_name[5] = '\0';
+
+    p_item->clpi.i_id = strtol( psz_name, NULL, 10 );
+
+    bs_skip( s, 32 );
+
+    bs_skip( s, 11 );
+    const bool b_angle = bs_read( s, 1 );
+
+    p_item->i_connection = bs_read( s, 4 );
+    p_item->clpi.i_stc_id = bs_read( s, 8 );
+    p_item->i_in_time = bs_read( s, 32 );
+    p_item->i_out_time = bs_read( s, 32 );
+    bs_skip( s, 64 );
+    bs_skip( s, 1 );
+    bs_skip( s, 7 );
+    p_item->i_still = bs_read( s, 8 );
+    p_item->i_still_time = bs_read( s, 16 );
+    if( p_item->i_still == BD_MPLS_PLAY_ITEM_STILL_NONE )
+        p_item->i_still_time = 0;
+    else if( p_item->i_still == BD_MPLS_PLAY_ITEM_STILL_INFINITE )
+        p_item->i_still_time = INT_MAX;
+
+    if( b_angle )
+    {
+        const int i_angle = bs_read( s, 8 );
+        bs_skip( s, 6 );
+        p_item->b_angle_different_audio = bs_read( s, 1 );
+        p_item->b_angle_seamless = bs_read( s, 1 );
+
+        p_item->p_clpi = calloc( i_angle, sizeof(*p_item->p_clpi) );
+        for( p_item->i_clpi = 0; p_item->i_clpi < i_angle; p_item->i_clpi++ )
+        {
+            if( !p_item->p_clpi )
+                break;
+
+            bd_mpls_clpi_t *p_clpi = &p_item->p_clpi[p_item->i_clpi];
+
+            char psz_name[5+1];
+            for( int j = 0; j < 5; j++ )
+                psz_name[j] = bs_read( s, 8 );
+            psz_name[5] = '\0';
+
+            p_clpi->i_id = strtol( psz_name, NULL, 10 );
+
+            bs_skip( s, 32 );
+
+            p_clpi->i_stc_id = bs_read( s, 8 );
+        }
+    }
+    else
+    {
+        p_item->i_clpi = 0;
+        p_item->p_clpi = NULL;
+        p_item->b_angle_different_audio = false;
+        p_item->b_angle_seamless = true;
+    }
+
+    /* STN Table */
+    bs_skip( s, 16 );  /* Length */
+    bs_skip( s, 16 );
+
+    const int i_video = bs_read( s, 8 );
+    const int i_audio = bs_read( s, 8 );
+    const int i_pg = bs_read( s, 8 );
+    const int i_ig = bs_read( s, 8 );
+    const int i_audio_2 = bs_read( s, 8 );
+    const int i_video_2 = bs_read( s, 8 );
+    const int i_pip_pg = bs_read( s, 8 );
+    bs_skip( s, 40 );
+
+    p_item->i_stream = 0;
+    p_item->p_stream = calloc( i_video + i_audio + i_pg + i_ig,
+                               sizeof(*p_item->p_stream) );
+
+    for( int j = 0; j < i_video; j++, p_item->i_stream++ )
+    {
+        if( !p_item->p_stream )
+            break;
+
+        bd_mpls_stream_Parse( &p_item->p_stream[p_item->i_stream], s,
+                              BD_MPLS_STREAM_CLASS_PRIMARY_VIDEO );
+    }
+    for( int j = 0; j < i_audio; j++, p_item->i_stream++ )
+    {
+        if( !p_item->p_stream )
+            break;
+
+        bd_mpls_stream_Parse( &p_item->p_stream[p_item->i_stream], s,
+                              BD_MPLS_STREAM_CLASS_PRIMARY_AUDIO );
+    }
+    for( int j = 0; j < i_pg; j++, p_item->i_stream++ )
+    {
+        if( !p_item->p_stream )
+            break;
+
+        bd_mpls_stream_Parse( &p_item->p_stream[p_item->i_stream], s,
+                              BD_MPLS_STREAM_CLASS_PG );
+    }
+    for( int j = 0; j < i_ig; j++, p_item->i_stream++ )
+    {
+        if( !p_item->p_stream )
+            break;
+
+        bd_mpls_stream_Parse( &p_item->p_stream[p_item->i_stream], s,
+                              BD_MPLS_STREAM_CLASS_IG );
+    }
+    for( int j = 0; j < i_audio_2; j++ )
+    {
+        /* TODO I need samples */
+    }
+    for( int j = 0; j < i_video_2; j++ )
+    {
+        /* TODO I need samples */
+    }
+    for( int j = 0; j < i_pip_pg; j++ )
+    {
+        /* TODO I need samples */
+    }
+
+    bs_skip( s, 8 * ( i_start + i_length ) - bs_pos( s ) );
+}
+
+void bd_mpls_sub_path_Parse( bd_mpls_sub_path_t *p_path, bs_t *s )
+{
+    const uint32_t i_length = bs_read( s, 32 );
+    const int i_start = bs_pos( s ) / 8;
+
+    bs_skip( s, 8 );
+    p_path->i_type = bs_read( s, 8 );
+    bs_skip( s, 15 );
+    p_path->b_repeat = bs_read( s, 1 );
+    bs_skip( s, 8 );
+    p_path->i_item = bs_read( s, 8 );
+
+    for( int j = 0; j < p_path->i_item; j++ )
+    {
+        const int i_length = bs_read( s, 16 );
+        const int i_start = bs_pos( s ) / 8;
+
+        /* TODO */
+
+        bs_skip( s, 8 * ( i_start + i_length ) - bs_pos( s ) );
+    }
+
+    bs_skip( s, 8 * ( i_start + i_length ) - bs_pos( s ) );
+}
+
+void bd_mpls_mark_Parse( bd_mpls_mark_t *p_mark, bs_t *s )
+{
+    bs_skip( s, 8 );
+    p_mark->i_type = bs_read( s, 8 );
+    p_mark->i_play_item_id = bs_read( s, 16 );
+    p_mark->i_time = bs_read( s, 32 );
+    p_mark->i_entry_es_pid = bs_read( s, 16 );
+    bs_skip( s, 32 );
+}
+
+void bd_mpls_Clean( bd_mpls_t *p_mpls )
+{
+    for( int i = 0; i < p_mpls->i_play_item; i++ )
+        bd_mpls_play_item_Clean( &p_mpls->p_play_item[i] );
+    free( p_mpls->p_play_item );
+
+    free( p_mpls->p_sub_path );
+
+    free( p_mpls->p_mark );
+}
+
+int bd_mpls_Parse( bd_mpls_t *p_mpls, bs_t *s, int i_id )
+{
+    const int i_start = bs_pos( s ) / 8;
+
+    /* */
+    if( bs_read( s, 32 ) != 0x4d504c53 )
+        return VLC_EGENERIC;
+    if( bs_read( s, 32 ) != 0x30323030 )
+        return VLC_EGENERIC;
+
+    const uint32_t i_play_item_start = bs_read( s, 32 );
+    const uint32_t i_mark_start = bs_read( s, 32 );
+    bs_skip( s, 32 );   /* Extension start */
+
+    /* */
+    p_mpls->i_id = i_id;
+
+    /* Read AppInfo: ignored */
+
+    /* Read Playlist */
+    bs_t ps = *s;
+    bs_skip( &ps, 8 * ( i_start + i_play_item_start ) - bs_pos( s ) );
+    bs_skip( &ps, 32 ); /* Length */
+    bs_skip( &ps, 16 );
+    const int i_play_item = bs_read( &ps, 16 );
+    const int i_sub_path = bs_read( &ps, 16 );
+
+    p_mpls->p_play_item = calloc( i_play_item, sizeof(*p_mpls->p_play_item) );
+    for( p_mpls->i_play_item = 0; p_mpls->i_play_item < i_play_item; p_mpls->i_play_item++ )
+    {
+        if( !p_mpls->p_play_item )
+            break;
+        bd_mpls_play_item_t *p_item = &p_mpls->p_play_item[p_mpls->i_play_item];
+
+        bd_mpls_play_item_Parse( p_item, &ps );
+    }
+    p_mpls->p_sub_path = calloc( i_sub_path, sizeof(*p_mpls->p_sub_path) );
+    for( p_mpls->i_sub_path = 0; p_mpls->i_sub_path < i_sub_path; p_mpls->i_sub_path++ )
+    {
+        if( !p_mpls->p_sub_path )
+            break;
+        bd_mpls_sub_path_t *p_sub = &p_mpls->p_sub_path[p_mpls->i_sub_path];
+
+        bd_mpls_sub_path_Parse( p_sub, &ps );
+    }
+
+    /* Read Mark */
+    bs_t ms = *s;
+    bs_skip( &ms, 8 * ( i_start + i_mark_start ) - bs_pos( s ) );
+    bs_skip( &ms, 32 );
+    const int i_mark = bs_read( &ms, 16 );
+
+    p_mpls->p_mark = calloc( i_mark, sizeof(*p_mpls->p_mark) );
+    for( p_mpls->i_mark = 0; p_mpls->i_mark < i_mark; p_mpls->i_mark++ )
+    {
+        if( !p_mpls->p_mark )
+            break;
+        bd_mpls_mark_t *p_mark = &p_mpls->p_mark[p_mpls->i_mark];
+
+        bd_mpls_mark_Parse( p_mark, &ms );
+    }
+
+    /* Read Extension: ignored */
+
+    return VLC_SUCCESS;
+}
diff --git a/modules/access/bd/mpls.h b/modules/access/bd/mpls.h
new file mode 100644 (file)
index 0000000..dfeb3f7
--- /dev/null
@@ -0,0 +1,182 @@
+/*****************************************************************************
+ * mpls.h: BluRay Disc MPLS
+ *****************************************************************************
+ * Copyright (C) 2009 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+typedef enum
+{
+    BD_MPLS_STREAM_TYPE_PLAY_ITEM = 0x01,
+    BD_MPLS_STREAM_TYPE_SUB_PATH = 0x02,
+    BD_MPLS_STREAM_TYPE_IN_MUX_SUB_PATH = 0x03,
+} bd_mpls_stream_type_t;
+typedef enum
+{
+    BD_MPLS_STREAM_CLASS_PRIMARY_VIDEO = 0,
+    BD_MPLS_STREAM_CLASS_PRIMARY_AUDIO,
+    BD_MPLS_STREAM_CLASS_PG,
+    BD_MPLS_STREAM_CLASS_IG,
+    BD_MPLS_STREAM_CLASS_SECONDARY_AUDIO,
+    BD_MPLS_STREAM_CLASS_SECONDARY_PIP_PG,
+} bd_mpls_stream_class_t;
+typedef enum
+{
+    BD_MPLS_STREAM_CHARSET_UNKNOWN = -1,
+
+    BD_MPLS_STREAM_CHARSET_UTF8 = 0x01,
+    BD_MPLS_STREAM_CHARSET_UTF16 = 0x02,
+    BD_MPLS_STREAM_CHARSET_SHIFT_JIS = 0x03,
+    BD_MPLS_STREAM_CHARSET_KSC = 0x04,
+    BD_MPLS_STREAM_CHARSET_GB18030 = 0x05,
+    BD_MPLS_STREAM_CHARSET_GB2312 = 0x06,
+    BD_MPLS_STREAM_CHARSET_BIG5 = 0x7,
+} bd_mpls_stream_charset_t;
+
+typedef struct
+{
+    /* Stream entry */
+    int i_type;             /* Type of the stream entry (bd_mpls_stream_type_t) */
+    int i_class;            /* Class of the stream entry (bd_mpls_stream_class_t) */
+    union
+    {
+        struct
+        {
+            int i_pid;              /* PID of the associated stream */
+        } play_item;
+        struct
+        {
+            int i_sub_path_id;      /* Index of the sub path entry */
+            int i_sub_clip_id;      /* Index of the sub clip entry (?) */
+            int i_pid;              /* PID of the associated stream */
+        } sub_path;
+        struct
+        {
+            int i_sub_path_id;      /* Index of the sub path entry */
+            int i_pid;              /* PID of the associated stream */
+        } in_mux_sub_path;
+    };
+
+    /* Stream attributes */
+    int  i_stream_type;     /* MPEG-2 TS stream_type */
+    char psz_language[3+1]; /* ISO-639 code, empty if NA */
+    int  i_charset;         /* For text stream only (bd_mpls_stream_attributes_charset_t) */
+} bd_mpls_stream_t;
+
+void bd_mpls_stream_Parse( bd_mpls_stream_t *p_stream, bs_t *s, int i_class );
+
+typedef enum
+{
+    BD_MPLS_PLAY_ITEM_CONNECTION_NOT_SEAMLESS = 0x01,
+    BD_MPLS_PLAY_ITEM_CONNECTION_SEAMLESS_5 = 0x05,
+    BD_MPLS_PLAY_ITEM_CONNECTION_SEAMLESS_6 = 0x06,
+} bd_mpls_play_item_connection_t;
+
+typedef enum
+{
+    BD_MPLS_PLAY_ITEM_STILL_NONE = 0x00,
+    BD_MPLS_PLAY_ITEM_STILL_FINITE = 0x01,
+    BD_MPLS_PLAY_ITEM_STILL_INFINITE = 0x02,
+} bd_mpls_play_item_still_t;
+
+typedef struct
+{
+    int     i_id;
+    int     i_stc_id;
+} bd_mpls_clpi_t;
+
+typedef struct
+{
+    int     i_connection;   /* Connection with previous play item (bd_mpls_play_item_connection_t) */
+    int64_t i_in_time;      /* Start time in 45kHz */
+    int64_t i_out_time;     /* Stop time in 45kHz */
+    int     i_still;        /* Still mode (bd_mpls_play_item_still_t) */
+    int     i_still_time;   /* Still time for BD_MPLS_PLAY_ITEM_STILL_FINITE (second?) */
+
+    /* Default clpi/angle */
+    bd_mpls_clpi_t  clpi;
+
+    /* Extra clpi (multiple angles) */
+    int             i_clpi;
+    bd_mpls_clpi_t *p_clpi;
+    bool            b_angle_different_audio;
+    bool            b_angle_seamless;
+
+
+    /* */
+    int              i_stream;
+    bd_mpls_stream_t *p_stream;
+
+} bd_mpls_play_item_t;
+void bd_mpls_play_item_Clean( bd_mpls_play_item_t *p_item );
+void bd_mpls_play_item_Parse( bd_mpls_play_item_t *p_item, bs_t *s );
+
+typedef enum
+{
+    BD_MPLS_SUB_PATH_TYPE_PRIMARY_AUDIO = 0x02,
+    BD_MPLS_SUB_PATH_TYPE_IG = 0x03,
+    BD_MPLS_SUB_PATH_TYPE_TEXT_SUB = 0x04,
+    BD_MPLS_SUB_PATH_TYPE_OUT_OF_MUX_AND_SYNC = 0x05,
+    BD_MPLS_SUB_PATH_TYPE_OUT_OF_MUX_AND_ASYNC = 0x06,
+    BD_MPLS_SUB_PATH_TYPE_IN_OF_MUX_AND_SYNC = 0x07,
+} bd_mpls_sub_path_type_t;
+typedef struct
+{
+    int  i_type;        /* Sub path type (bd_mpls_sub_path_type_t) */
+    bool b_repeat;      /* Repeated sub-path */
+
+    int  i_item;
+    /* TODO
+    bd_mpls_sub_play_item_t *p_item;
+    */
+} bd_mpls_sub_path_t;
+void bd_mpls_sub_path_Parse( bd_mpls_sub_path_t *p_path, bs_t *s );
+
+typedef enum
+{
+    BD_MPLS_MARK_TYPE_RESUME = 0x00,
+    BD_MPLS_MARK_TYPE_BOOKMARK = 0x01,
+    BD_MPLS_MARK_TYPE_SKIP = 0x02,
+} bd_mpls_mark_type_t;
+
+typedef struct
+{
+    int     i_type;             /* Type of the mark (bd_mpls_mark_type_t) */
+    int     i_play_item_id;     /* Play item ID */
+    int64_t i_time;             /* Time of the mark in 45kHz */
+    int     i_entry_es_pid;     /* Entry ES PID */
+} bd_mpls_mark_t;
+void bd_mpls_mark_Parse( bd_mpls_mark_t *p_mark, bs_t *s );
+
+typedef struct
+{
+    int                 i_id;
+
+    int                 i_play_item;
+    bd_mpls_play_item_t *p_play_item;
+
+    int                 i_sub_path;
+    bd_mpls_sub_path_t  *p_sub_path;
+
+    int                 i_mark;
+    bd_mpls_mark_t      *p_mark;
+} bd_mpls_t;
+void bd_mpls_Clean( bd_mpls_t *p_mpls );
+int bd_mpls_Parse( bd_mpls_t *p_mpls, bs_t *s, int i_id );
+