]> git.sesse.net Git - vlc/commitdiff
* modules/access/vcdx/*: Brand new VCD input module using libcdio, libvcd and libvcdi...
authorGildas Bazin <gbazin@videolan.org>
Sat, 4 Oct 2003 18:55:13 +0000 (18:55 +0000)
committerGildas Bazin <gbazin@videolan.org>
Sat, 4 Oct 2003 18:55:13 +0000 (18:55 +0000)
* modules/access/cddax.c: Brand new CDDA input module using libcdio. Courtesy of Rocky Bernstein.
* modules/LIST: update.

16 files changed:
configure.ac
modules/LIST
modules/access/Modules.am
modules/access/cddax.c [new file with mode: 0644]
modules/access/vcdx/.cvsignore [new file with mode: 0644]
modules/access/vcdx/Modules.am [new file with mode: 0644]
modules/access/vcdx/access.c [new file with mode: 0644]
modules/access/vcdx/cdrom.c [new file with mode: 0644]
modules/access/vcdx/cdrom.h [new file with mode: 0644]
modules/access/vcdx/demux.c [new file with mode: 0644]
modules/access/vcdx/intf.c [new file with mode: 0644]
modules/access/vcdx/intf.h [new file with mode: 0644]
modules/access/vcdx/vcd.c [new file with mode: 0644]
modules/access/vcdx/vcd.h [new file with mode: 0644]
modules/access/vcdx/vcdplayer.c [new file with mode: 0644]
modules/access/vcdx/vcdplayer.h [new file with mode: 0644]

index 0b7525a7f46457632ff834a40e6f035827be8347..887c788d192961587c2b8cb194e30fd27071eacd 100644 (file)
@@ -1,5 +1,5 @@
 dnl Autoconf settings for vlc
-dnl $Id: configure.ac,v 1.83 2003/10/04 11:59:59 gbazin Exp $
+dnl $Id: configure.ac,v 1.84 2003/10/04 18:55:12 gbazin Exp $
 
 AC_INIT(vlc,0.6.3-cvs)
 
@@ -79,7 +79,7 @@ case "${target_os}" in
   bsdi*)
     SYS=bsdi
     CFLAGS_save="${CFLAGS_save} -pthread"; CFLAGS="${CFLAGS_save}"
-    AX_ADD_LDFLAGS([dvd dvdcss vcd cdda],[-ldvd])
+    AX_ADD_LDFLAGS([dvd dvdcss vcd cdda vcdx cddax],[-ldvd])
     ;;
   *bsd*)
     SYS="${target_os}"
@@ -1439,6 +1439,29 @@ then
   fi
 fi
 
+dnl
+dnl  VCDX and CDX modules
+dnl
+AC_ARG_ENABLE(vcdx,
+  [  --enable-vcdx            VCD support with Navigation for Linux and Win32 (default enabled)])
+
+if test "${enable_vcdx}" != "no"
+then
+  PKG_CHECK_MODULES(LIBCDIO, libcdio >= 0.63,
+    enable_cdda="no"
+    AX_ADD_LDFLAGS([cddax],[$LIBCDIO_LIBS])
+    AX_ADD_CFLAGS([cddax],[$LIBCDIO_CFLAGS])
+    AX_ADD_PLUGINS([cddax]), 
+    [AC_MSG_WARN(libcdio library not found)])
+
+  PKG_CHECK_MODULES(VCDINFO, libvcdinfo >= 0.7.18-cdio,
+    enable_vcd="no"
+    AX_ADD_LDFLAGS([vcdx],[$VCDINFO_LIBS])
+    AX_ADD_CFLAGS([vcdx],[$VCDINFO_CFLAGS])
+    AX_ADD_PLUGINS([vcdx]), 
+[AC_MSG_WARN(vcdinfo library not found)])
+fi
+
 dnl
 dnl  Satellite input module
 dnl
@@ -3360,6 +3383,7 @@ AC_OUTPUT([
   modules/access/satellite/Makefile
   modules/access/v4l/Makefile
   modules/access/vcd/Makefile
+  modules/access/vcdx/Makefile
   modules/access_output/Makefile
   modules/audio_filter/Makefile
   modules/audio_filter/channel_mixer/Makefile
index 4d4c8e759e64721069fb63a50f03896f9204b9b7..7c9666c19e4e31d1facccc7f5b0b699b57e7d3c8 100644 (file)
@@ -1,10 +1,8 @@
 List of vlc plugins (221)
-$Id: LIST,v 1.12 2003/09/18 16:46:02 lool Exp $
+$Id: LIST,v 1.13 2003/10/04 18:55:13 gbazin Exp $
 
- * a52: A/52 basic parser
+ * a52: A/52 basic parser/packetizer
 
- * a52old: Old A/52 decoder
  * a52sys: A/52 decapsulation demuxer.
 
  * a52tofloat32: A/52 audio converter & decoder plugin, using liba52
@@ -63,6 +61,8 @@ $Id: LIST,v 1.12 2003/09/18 16:46:02 lool Exp $
 
  * cdda: input module to read audio CDs
 
+ * cddax: input module to read audio CDs vi libcdio
+
  * cinepak: Cinepack video decoder
 
  * clone: Clone video filter
@@ -85,15 +85,9 @@ $Id: LIST,v 1.12 2003/09/18 16:46:02 lool Exp $
 
  * distort: miscellaneous image effects filter.
 
- * downmix3dn: 3D Now! accelerated version of downmix.
-
- * downmix: downmix module, used by the AC3 decoder.
-
- * downmixsse: SSE accelerated version of downmix.
-
  * dshow: DirectShow access plugin for encoding cards under Windows
 
- * dts: DTS basic parser
+ * dts: DTS basic parser/packetizer
 
  * dtstospdif: Audio converter that encapsulates DTS into S/PDIF
 
@@ -196,22 +190,6 @@ $Id: LIST,v 1.12 2003/09/18 16:46:02 lool Exp $
  
  * id3tag: ID3 tag parser/skipper using libid3tag
  
- * idctaltivec: AltiVec accelerated version of idct.
- * idctclassic: another version of idct.
-
- * idct: inverse DCT module, used by mpeg_video_old
-
- * idctmmxext: MMX EXT accelerated version of idct.
- * idctmmx: MMX accelerated version of idct.
- * imdct3dn: 3D Now! Accelerated A/52 DCT
-
- * imdct: IMDCT module for the A/52 decoder
-
- * imdctsse: SSE accelerated A/52 DCT
-
  * invert: inverse video filter.
 
  * ipv4: IPv4 network abstraction layer
@@ -254,26 +232,14 @@ $Id: LIST,v 1.12 2003/09/18 16:46:02 lool Exp $
 
  * mkv: Matroska demuxer
 
- * motion3dnow: 3D Now! accelerated version of motion.
- * motionaltivec: AltiVec accelerated version of motion.
-
  * motionblur: Motion blur filter
 
- * motionmmxext: MMX EXT accelerated version of motion.
- * motionmmx: MMX accelerated version of motion.
-
- * motion: motion compensation module for mpeg_video_old
-
  * mp4: MP4 file input module
 
- * mpeg_audio: MPEG audio parser and packetizer
+ * mpeg_audio: MPEG audio parser/packetizer
 
  * mpeg_system: helper module for TS, PS and PES management
 
- * mpeg_video_old: MPEG and MPEG2 video decoder.
-
  * mpga: MPEG-I/II Audio demuxer
 
  * mpgatofixed32: MPEG-1 & 2 audio layer I,II,III audio decoder using MAD
@@ -300,20 +266,14 @@ $Id: LIST,v 1.12 2003/09/18 16:46:02 lool Exp $
 
  * oss: audio output module using the OSS /dev/dsp interface.
  
- * packetizer_a52: A/52 audio packetizer
-
  * packetizer_copy: Simple copy packetizer
 
  * packetizer_mpeg4audio: MPEG4 audio packetizer
  
  * packetizer_mpeg4video: MPEG4 video packetizer
 
- * packetizer_mpegaudio: MPEG audio packetizer
  * packetizer_mpegvideo: MPEG video packetizer
  
- * packetizer_vorbis: Vorbis packetizer
-
  * pda: interface for iPaq using the Gtk2+ widget set.
 
  * postprocessing_c: Video Postprocessing module
@@ -378,7 +338,7 @@ $Id: LIST,v 1.12 2003/09/18 16:46:02 lool Exp $
  
  * stream_out_transcode: audio & video transcoder using ffmpeg
 
- * subsdec: Another SPU decoder
+ * subsdec: Another SPU decoder for text subtitles
 
  * svgalib: SGVAlib output plugin
 
@@ -392,7 +352,7 @@ $Id: LIST,v 1.12 2003/09/18 16:46:02 lool Exp $
 
  * test4: Stress test module
 
- * theora: a theora video decoder using the libtheora library.
+ * theora: a theora video decoder/packetizer using the libtheora library.
 
  * transform: filter for horizontal and vertical image flips and
                      90° rotations.
@@ -419,9 +379,11 @@ $Id: LIST,v 1.12 2003/09/18 16:46:02 lool Exp $
 
  * vcd: input module for accessing Video CDs.
 
+ * vcdx: input module for accessing Video CDs with navigation & stills
+
  * visual: visualisation system
 
- * vorbis: a vorbis audio decoder using the libvorbis library.
+ * vorbis: a vorbis audio decoder/packetizer using the libvorbis library.
 
  * vout_directx: video output module using the DirectX API.
 
index 32f39d8574273f24ba61afef2f2f680cf026b202..686321f14e4ea90161101281ce4eee301793b94c 100644 (file)
@@ -10,3 +10,7 @@ SOURCES_cdda = \
         vcd/cdrom.h \
         vcd/cdrom_internals.h \
         $(NULL)
+SOURCES_cddax = \
+        cddax.c \
+        vcdx/cdrom.c \
+        vcdx/cdrom.h
diff --git a/modules/access/cddax.c b/modules/access/cddax.c
new file mode 100644 (file)
index 0000000..371b8c8
--- /dev/null
@@ -0,0 +1,521 @@
+/*****************************************************************************
+ * cddax.c : CD digital audio input module for vlc using libcdio
+ *****************************************************************************
+ * Copyright (C) 2000 VideoLAN
+ * $Id: cddax.c,v 1.1 2003/10/04 18:55:13 gbazin Exp $
+ *
+ * Authors: Laurent Aimar <fenrir@via.ecp.fr>
+ *          Gildas Bazin <gbazin@netcourrier.com>
+ *          Rocky Bernstein <rocky@panix.com> 
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <vlc/vlc.h>
+#include <vlc/input.h>
+#include <sys/types.h>
+#include <cdio/cdio.h>
+#include <cdio/cd_types.h>
+
+#include "codecs.h"
+
+#ifdef HAVE_UNISTD_H
+#   include <unistd.h>
+#endif
+
+#include <string.h>
+
+#include "vcdx/cdrom.h"
+
+/* how many blocks CDDAOpen will read in each loop */
+#define CDDA_BLOCKS_ONCE 1
+#define CDDA_DATA_ONCE   (CDDA_BLOCKS_ONCE * CDIO_CD_FRAMESIZE_RAW)
+
+/*****************************************************************************
+ * cdda_data_t: CD audio information
+ *****************************************************************************/
+typedef struct cdda_data_s
+{
+    cddev_t     *p_cddev;                           /* CD device descriptor */
+    int         i_nb_tracks;                        /* Nb of tracks (titles) */
+    int         i_track;                                    /* Current track */
+    lsn_t       i_sector;                                  /* Current Sector */
+    lsn_t *     p_sectors;                                  /* Track sectors */
+    vlc_bool_t  b_end_of_track;           /* If the end of track was reached */
+
+} cdda_data_t;
+
+struct demux_sys_t
+{
+    es_descriptor_t *p_es;
+    mtime_t         i_pts;
+};
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int  CDDAOpen         ( vlc_object_t * );
+static void CDDAClose        ( vlc_object_t * );
+static int  CDDARead         ( input_thread_t *, byte_t *, size_t );
+static void CDDASeek         ( input_thread_t *, off_t );
+static int  CDDASetArea      ( input_thread_t *, input_area_t * );
+static int  CDDASetProgram   ( input_thread_t *, pgrm_descriptor_t * );
+
+static int  CDDAOpenDemux    ( vlc_object_t * );
+static void CDDACloseDemux   ( vlc_object_t * );
+static int  CDDADemux        ( input_thread_t * p_input );
+
+/*****************************************************************************
+ * Module descriptior
+ *****************************************************************************/
+#define CACHING_TEXT N_("Caching value in ms")
+#define CACHING_LONGTEXT N_( \
+    "Allows you to modify the default caching value for cdda streams. This " \
+    "value should be set in miliseconds units." )
+
+vlc_module_begin();
+    set_description( _("CD Audio input") );
+    set_capability( "access", 75 /* slightly higher than cdda */ );
+    add_integer( "cddax-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
+    set_callbacks( CDDAOpen, CDDAClose );
+    add_shortcut( "cdda" );
+
+    add_submodule();
+        set_description( _("CD Audio demux") );
+        set_capability( "demux", 0 );
+        set_callbacks( CDDAOpenDemux, CDDACloseDemux );
+        add_shortcut( "cdda" );
+vlc_module_end();
+
+/*****************************************************************************
+ * CDDAOpen: open cdda
+ *****************************************************************************/
+static int CDDAOpen( vlc_object_t *p_this )
+{
+    input_thread_t *        p_input = (input_thread_t *)p_this;
+    char *                  psz_orig;
+    char *                  psz_parser;
+    char *                  psz_source;
+    cdda_data_t *           p_cdda;
+    int                     i;
+    input_area_t *          p_area;
+    int                     i_title = 1;
+    cddev_t                 *p_cddev;
+
+    /* parse the options passed in command line : */
+    psz_orig = psz_parser = psz_source = strdup( p_input->psz_name );
+
+    if( !psz_orig )
+    {
+        return( -1 );
+    }
+
+    while( *psz_parser && *psz_parser != '@' )
+    {
+        psz_parser++;
+    }
+
+    if( *psz_parser == '@' )
+    {
+        /* Found options */
+        *psz_parser = '\0';
+        ++psz_parser;
+
+        if ('T' == *psz_parser || 't' == *psz_parser ) 
+            ++psz_parser;
+          
+        i_title = (int)strtol( psz_parser, NULL, 10 );
+        i_title = i_title ? i_title : 1;
+    }
+
+    if( !*psz_source ) {
+      /* No source specified, so figure it out. */
+      if( !p_input->psz_access ) {
+        free( psz_orig );
+        return -1;
+      }
+      psz_source = config_GetPsz( p_input, MODULE_STRING "-device" );
+      
+      if( !psz_source ) {
+        /* Scan for a CD with a VCD in it. */
+        char **cd_drives = 
+          cdio_get_devices_with_cap(NULL,  CDIO_FS_AUDIO, false);
+        if (NULL == cd_drives) return -1;
+        if (cd_drives[0] == NULL) {
+          cdio_free_device_list(cd_drives);
+          return -1;
+        }
+        psz_source = strdup(cd_drives[0]);
+        cdio_free_device_list(cd_drives);
+      }
+    }
+
+    /* Open CDDA */
+    if( !(p_cddev = ioctl_Open( p_this, psz_source )) )
+    {
+        msg_Warn( p_input, "could not open %s", psz_source );
+        free( psz_source );
+        return -1;
+    }
+    free( psz_source );
+
+    p_cdda = malloc( sizeof(cdda_data_t) );
+    if( p_cdda == NULL )
+    {
+        msg_Err( p_input, "out of memory" );
+        free( psz_source );
+        return -1;
+    }
+
+    p_cdda->p_cddev = p_cddev;
+    p_input->p_access_data = (void *)p_cdda;
+
+    p_input->i_mtu = CDDA_DATA_ONCE;
+
+    /* We read the Table Of Content information */
+    p_cdda->i_nb_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
+                              p_cdda->p_cddev->cdio, &p_cdda->p_sectors );
+    if( p_cdda->i_nb_tracks < 0 )
+        msg_Err( p_input, "unable to count tracks" );
+    else if( p_cdda->i_nb_tracks <= 0 )
+        msg_Err( p_input, "no audio tracks found" );
+
+    if( p_cdda->i_nb_tracks <= 1)
+    {
+        ioctl_Close( p_cdda->p_cddev );
+        free( p_cdda );
+        return -1;
+    }
+
+    if( i_title >= p_cdda->i_nb_tracks || i_title < 1 )
+        i_title = 1;
+
+    /* Set stream and area data */
+    vlc_mutex_lock( &p_input->stream.stream_lock );
+
+    /* Initialize ES structures */
+    input_InitStream( p_input, 0 );
+
+    /* cdda input method */
+    p_input->stream.i_method = INPUT_METHOD_CDDA;
+
+    p_input->stream.b_pace_control = 1;
+    p_input->stream.b_seekable = 1;
+    p_input->stream.i_mux_rate = 44100 * 4 / 50;
+
+#define area p_input->stream.pp_areas
+    for( i = 1 ; i <= p_cdda->i_nb_tracks ; i++ )
+    {
+        input_AddArea( p_input, i, 1 );
+
+        /* Absolute start offset and size */
+        area[i]->i_start =
+            (off_t)p_cdda->p_sectors[i-1] * (off_t)CDIO_CD_FRAMESIZE_RAW;
+        area[i]->i_size =
+            (off_t)(p_cdda->p_sectors[i] - p_cdda->p_sectors[i-1])
+            * (off_t)CDIO_CD_FRAMESIZE_RAW;
+    }
+#undef area
+
+    p_area = p_input->stream.pp_areas[i_title];
+
+    CDDASetArea( p_input, p_area );
+
+    vlc_mutex_unlock( &p_input->stream.stream_lock );
+
+    if( !p_input->psz_demux || !*p_input->psz_demux )
+    {
+        p_input->psz_demux = "cdda";
+    }
+
+    p_input->pf_read = CDDARead;
+    p_input->pf_seek = CDDASeek;
+    p_input->pf_set_area = CDDASetArea;
+    p_input->pf_set_program = CDDASetProgram;
+
+    /* Update default_pts to a suitable value for cdda access */
+    p_input->i_pts_delay = config_GetInt( p_input, "cddax-caching" ) * 1000;
+
+    return 0;
+}
+
+/*****************************************************************************
+ * CDDAClose: closes cdda
+ *****************************************************************************/
+static void CDDAClose( vlc_object_t *p_this )
+{
+    input_thread_t *   p_input = (input_thread_t *)p_this;
+    cdda_data_t *p_cdda = (cdda_data_t *)p_input->p_access_data;
+
+    ioctl_Close( p_cdda->p_cddev );
+    free( p_cdda );
+}
+
+/*****************************************************************************
+ * CDDARead: reads from the CDDA into PES packets.
+ *****************************************************************************
+ * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
+ * bytes.
+ *****************************************************************************/
+static int CDDARead( input_thread_t * p_input, byte_t * p_buffer,
+                     size_t i_len )
+{
+    cdda_data_t *           p_cdda;
+    int                     i_blocks;
+    int                     i_index;
+    int                     i_read;
+
+    p_cdda = (cdda_data_t *)p_input->p_access_data;
+
+    i_read = 0;
+
+    /* Compute the number of blocks we have to read */
+
+    i_blocks = i_len / CDIO_CD_FRAMESIZE_RAW;
+
+    for ( i_index = 0; i_index < i_blocks; i_index++ )
+    {
+
+      if (cdio_read_audio_sector(p_cdda->p_cddev->cdio, p_buffer, 
+                                 p_cdda->i_sector) != 0)
+        {
+          msg_Err( p_input, "could not read sector %d", p_cdda->i_sector );
+          return -1;
+        }
+
+        p_cdda->i_sector ++;
+        if ( p_cdda->i_sector == p_cdda->p_sectors[p_cdda->i_track + 1] )
+        {
+            input_area_t *p_area;
+
+            if ( p_cdda->i_track >= p_cdda->i_nb_tracks - 1 )
+                return 0; /* EOF */
+
+            vlc_mutex_lock( &p_input->stream.stream_lock );
+            p_area = p_input->stream.pp_areas[
+                    p_input->stream.p_selected_area->i_id + 1 ];
+
+            msg_Dbg( p_input, "new title" );
+
+            p_area->i_part = 1;
+            CDDASetArea( p_input, p_area );
+            vlc_mutex_unlock( &p_input->stream.stream_lock );
+        }
+        i_read += CDIO_CD_FRAMESIZE_RAW;
+    }
+
+    if ( i_len % CDIO_CD_FRAMESIZE_RAW ) /* this should not happen */
+    {
+        msg_Err( p_input, "must read full sectors" );
+    }
+
+    return i_read;
+}
+
+/*****************************************************************************
+ * CDDASetProgram: Does nothing since a CDDA is mono_program
+ *****************************************************************************/
+static int CDDASetProgram( input_thread_t * p_input,
+                           pgrm_descriptor_t * p_program)
+{
+    return 0;
+}
+
+/*****************************************************************************
+ * CDDASetArea: initialize input data for title x.
+ * It should be called for each user navigation request.
+ ****************************************************************************/
+static int CDDASetArea( input_thread_t * p_input, input_area_t * p_area )
+{
+    cdda_data_t *p_cdda = (cdda_data_t*)p_input->p_access_data;
+    vlc_value_t val;
+
+    /* we can't use the interface slider until initilization is complete */
+    p_input->stream.b_seekable = 0;
+
+    if( p_area != p_input->stream.p_selected_area )
+    {
+        /* Change the default area */
+        p_input->stream.p_selected_area = p_area;
+
+        /* Change the current track */
+        p_cdda->i_track = p_area->i_id - 1;
+        p_cdda->i_sector = p_cdda->p_sectors[p_cdda->i_track];
+
+        /* Update the navigation variables without triggering a callback */
+        val.i_int = p_area->i_id;
+        var_Change( p_input, "title", VLC_VAR_SETVALUE, &val, NULL );
+    }
+
+    p_cdda->i_sector = p_cdda->p_sectors[p_cdda->i_track];
+
+    p_input->stream.p_selected_area->i_tell =
+        (off_t)p_cdda->i_sector * (off_t)CDIO_CD_FRAMESIZE_RAW
+         - p_input->stream.p_selected_area->i_start;
+
+    /* warn interface that something has changed */
+    p_input->stream.b_seekable = 1;
+    p_input->stream.b_changed = 1;
+
+    return 0;
+}
+
+/****************************************************************************
+ * CDDASeek
+ ****************************************************************************/
+static void CDDASeek( input_thread_t * p_input, off_t i_off )
+{
+    cdda_data_t * p_cdda;
+
+    p_cdda = (cdda_data_t *) p_input->p_access_data;
+
+    p_cdda->i_sector = p_cdda->p_sectors[p_cdda->i_track]
+                       + i_off / (off_t)CDIO_CD_FRAMESIZE_RAW;
+
+    vlc_mutex_lock( &p_input->stream.stream_lock );
+    p_input->stream.p_selected_area->i_tell =
+        (off_t)p_cdda->i_sector * (off_t)CDIO_CD_FRAMESIZE_RAW
+         - p_input->stream.p_selected_area->i_start;
+    vlc_mutex_unlock( &p_input->stream.stream_lock );
+}
+
+/****************************************************************************
+ * Demux Part
+ ****************************************************************************/
+static int  CDDAOpenDemux    ( vlc_object_t * p_this)
+{
+    input_thread_t *p_input = (input_thread_t *)p_this;
+    demux_sys_t    *p_demux;
+    WAVEFORMATEX   *p_wf;
+
+    if( p_input->stream.i_method != INPUT_METHOD_CDDA )
+    {
+        return VLC_EGENERIC;
+    }
+
+    p_demux = malloc( sizeof( es_descriptor_t ) );
+    p_demux->i_pts = 0;
+    p_demux->p_es = NULL;
+
+    p_input->pf_demux  = CDDADemux;
+    p_input->pf_rewind = NULL;
+    p_input->p_demux_data = p_demux;
+
+    vlc_mutex_lock( &p_input->stream.stream_lock );
+    if( input_AddProgram( p_input, 0, 0) == NULL )
+    {
+        msg_Err( p_input, "cannot add program" );
+        free( p_input->p_demux_data );
+        return( -1 );
+    }
+    p_input->stream.pp_programs[0]->b_is_ok = 0;
+    p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
+
+    /* create our ES */ 
+    p_demux->p_es = input_AddES( p_input, 
+                                 p_input->stream.p_selected_program, 
+                                 1 /* id */, AUDIO_ES, NULL, 0 );
+    if( !p_demux->p_es )
+    {
+        vlc_mutex_unlock( &p_input->stream.stream_lock );
+        msg_Err( p_input, "out of memory" );
+        free( p_input->p_demux_data );
+        return( -1 );
+    }
+    p_demux->p_es->i_stream_id = 1;
+    p_demux->p_es->i_fourcc = VLC_FOURCC('a','r','a','w');
+
+    p_demux->p_es->p_waveformatex = p_wf = malloc( sizeof( WAVEFORMATEX ) );
+    p_wf->wFormatTag = WAVE_FORMAT_PCM;
+    p_wf->nChannels = 2;
+    p_wf->nSamplesPerSec = 44100;
+    p_wf->nAvgBytesPerSec = 2 * 44100 * 2;
+    p_wf->nBlockAlign = 4;
+    p_wf->wBitsPerSample = 16;
+    p_wf->cbSize = 0;
+
+    input_SelectES( p_input, p_demux->p_es );
+
+    p_input->stream.p_selected_program->b_is_ok = 1;
+    vlc_mutex_unlock( &p_input->stream.stream_lock );
+
+    return VLC_SUCCESS;
+}
+
+static void CDDACloseDemux( vlc_object_t * p_this)
+{
+    input_thread_t *p_input = (input_thread_t*)p_this;
+    demux_sys_t    *p_demux = (demux_sys_t*)p_input->p_demux_data;
+
+    free( p_demux );
+    p_input->p_demux_data = NULL;
+    return;
+}
+
+static int  CDDADemux( input_thread_t * p_input )
+{
+    demux_sys_t    *p_demux = (demux_sys_t*)p_input->p_demux_data;
+    ssize_t         i_read;
+    data_packet_t * p_data;
+    pes_packet_t *  p_pes;
+
+    input_ClockManageRef( p_input,
+                          p_input->stream.p_selected_program,
+                          p_demux->i_pts );
+
+    i_read = input_SplitBuffer( p_input, &p_data, CDIO_CD_FRAMESIZE_RAW );
+    if( i_read <= 0 )
+    {
+        return 0; // EOF
+    }
+
+    p_pes = input_NewPES( p_input->p_method_data );
+
+    if( p_pes == NULL )
+    {
+        msg_Err( p_input, "out of memory" );
+        input_DeletePacket( p_input->p_method_data, p_data );
+        return -1;
+    }
+
+    p_pes->i_rate = p_input->stream.control.i_rate;
+    p_pes->p_first = p_pes->p_last = p_data;
+    p_pes->i_nb_data = 1;
+    p_pes->i_pes_size = i_read;
+
+    p_pes->i_dts =
+        p_pes->i_pts = input_ClockGetTS( p_input,
+                                         p_input->stream.p_selected_program,
+                                         p_demux->i_pts );
+
+    if( p_demux->p_es->p_decoder_fifo )
+    {
+        input_DecodePES( p_demux->p_es->p_decoder_fifo, p_pes );
+    }
+    else
+    {
+        input_DeletePES( p_input->p_method_data, p_pes );
+    }
+
+    p_demux->i_pts += ((mtime_t)90000) * i_read
+                      / (mtime_t)44100 / 4 /* stereo 16 bits */;
+    return 1;
+}
diff --git a/modules/access/vcdx/.cvsignore b/modules/access/vcdx/.cvsignore
new file mode 100644 (file)
index 0000000..d0721f6
--- /dev/null
@@ -0,0 +1,9 @@
+.deps
+.dirstamp
+Makefile
+Makefile.am
+Makefile.in
+*.dll
+*.dylib
+*.sl
+*.so
diff --git a/modules/access/vcdx/Modules.am b/modules/access/vcdx/Modules.am
new file mode 100644 (file)
index 0000000..c9dc734
--- /dev/null
@@ -0,0 +1,12 @@
+SOURCES_vcdx = \
+       intf.c \
+       intf.h \
+       vcd.c \
+       vcd.h \
+       vcdplayer.h \
+       vcdplayer.c \
+       cdrom.c \
+       cdrom.h \
+       demux.c \
+       access.c \
+       $(NULL)
diff --git a/modules/access/vcdx/access.c b/modules/access/vcdx/access.c
new file mode 100644 (file)
index 0000000..bc99a73
--- /dev/null
@@ -0,0 +1,1084 @@
+/*****************************************************************************
+ * vcd.c : VCD input module for vlc
+ *         using libcdio, libvcd and libvcdinfo. vlc-specific things tend
+ *         to go here.
+ *****************************************************************************
+ * Copyright (C) 2000 VideoLAN
+ * $Id: access.c,v 1.1 2003/10/04 18:55:13 gbazin Exp $
+ *
+ * Authors: Johan Bilien <jobi@via.ecp.fr>
+ *          Rocky Bernstein <rocky@panix.com> 
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#if 0 // Disabled until this is working
+#include <vlc/vlc.h>
+#include <vlc/input.h>
+
+#include "../../demux/mpeg/system.h"
+#include "vcd.h"
+#include "intf.h"
+#include "vcdplayer.h"
+
+#include <cdio/cdio.h>
+#include <cdio/logging.h>
+#include <cdio/util.h>
+#include <libvcd/info.h>
+#include <libvcd/logging.h>
+
+#include "cdrom.h"
+
+/* how many blocks VCDRead will read in each loop */
+#define VCD_BLOCKS_ONCE 20
+#define VCD_DATA_ONCE   (VCD_BLOCKS_ONCE * M2F2_SECTOR_SIZE)
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+
+/* First those which are accessed from outside (via pointers). */
+static int  VCDOpen         ( vlc_object_t * );
+static void VCDClose        ( vlc_object_t * );
+static int  VCDRead         ( input_thread_t *, byte_t *, size_t );
+static int  VCDRead         ( input_thread_t *, byte_t *, size_t );
+static int  VCDSetProgram   ( input_thread_t *, pgrm_descriptor_t * );
+
+/* Now those which are strictly internal */
+static void VCDSetOrigin    ( input_thread_t *, lsn_t origin_lsn, 
+                              lsn_t cur_lsn, lsn_t end_lsn, 
+                              int cur_entry, track_t track );
+static int  VCDEntryPoints  ( input_thread_t * );
+static int  VCDLIDs         ( input_thread_t * );
+static int  VCDSegments     ( input_thread_t * );
+static void VCDTracks       ( input_thread_t * );
+static int  VCDReadSector   ( vlc_object_t *p_this, 
+                              const vcdinfo_obj_t *p_vcd, lsn_t cur_lsn, 
+                              byte_t * p_buffer );
+static char *VCDParse       ( input_thread_t *, 
+                              /*out*/ vcdinfo_itemid_t * p_itemid );
+
+static void VCDUpdateVar( input_thread_t *p_input, int i_entry, int i_action,
+                          const char *varname, const char *label );
+
+static vcdinfo_obj_t *vcd_Open   ( vlc_object_t *p_this, const char *psz_dev );
+
+static int debug_callback   ( vlc_object_t *p_this, const char *psz_name,
+                               vlc_value_t oldval, vlc_value_t val, 
+                               void *p_data );
+
+#define DEBUG_TEXT N_("set debug mask for additional debugging.")
+#define DEBUG_LONGTEXT N_( \
+    "This integer when viewed in binary is a debugging mask\n" \
+    "MRL             1\n" \
+    "external call   2\n" \
+    "all calls       4\n" \
+    "LSN             8\n" \
+    "PBC      (10)  16\n" \
+    "libcdio  (20)  32\n" \
+    "seeks    (40)  64\n" \
+    "still    (80) 128\n" \
+    "vcdinfo (100) 256\n" )
+
+/****************************************************************************
+ * Private functions
+ ****************************************************************************/
+/* FIXME: This variable is a hack. Would be nice to eliminate the 
+   global-ness. */
+static input_thread_t *p_vcd_input = NULL;
+
+int
+vcd_debug_callback   ( vlc_object_t *p_this, const char *psz_name,
+                       vlc_value_t oldval, vlc_value_t val, void *p_data )
+{
+  thread_vcd_data_t *p_vcd;
+
+  if (NULL == p_vcd_input) return VLC_EGENERIC;
+  
+  p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
+
+  if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
+    msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d", 
+             p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
+  }
+  p_vcd->i_debug = val.i_int;
+  return VLC_SUCCESS;
+}
+
+/* process messages that originate from libcdio. */
+static void
+cdio_log_handler (cdio_log_level_t level, const char message[])
+{
+  thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
+  switch (level) {
+  case CDIO_LOG_DEBUG:
+  case CDIO_LOG_INFO:
+    if (p_vcd->i_debug & INPUT_DBG_CDIO) 
+      msg_Dbg( p_vcd_input, message);
+    break;
+  case CDIO_LOG_WARN:
+    msg_Warn( p_vcd_input, message);
+    break;
+  case CDIO_LOG_ERROR:
+  case CDIO_LOG_ASSERT:
+    msg_Err( p_vcd_input, message);
+    break;
+  default:
+    msg_Warn( p_vcd_input, message,
+            _("The above message had unknown vcdimager log level"), 
+            level);
+  }
+  return;
+}
+
+/* process messages that originate from vcdinfo. */
+static void
+vcd_log_handler (vcd_log_level_t level, const char message[])
+{
+  thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
+  switch (level) {
+  case VCD_LOG_DEBUG:
+  case VCD_LOG_INFO:
+    if (p_vcd->i_debug & INPUT_DBG_VCDINFO)
+      msg_Dbg( p_vcd_input, message);
+    break;
+  case VCD_LOG_WARN:
+    msg_Warn( p_vcd_input, message);
+    break;
+  case VCD_LOG_ERROR:
+  case VCD_LOG_ASSERT:
+    msg_Err( p_vcd_input, message);
+    break;
+  default:
+    msg_Warn( p_vcd_input, "%s\n%s %d", message,
+            _("The above message had unknown vcdimager log level"), 
+            level);
+  }
+  return;
+}
+
+/*
+ * Data reading functions
+ */
+
+/*****************************************************************************
+  VCDOpen: open VCD.
+  read in meta-information about VCD: the number of tracks, segments, 
+  entries, size and starting information. Then set up state variables so
+  that we read/seek starting at the location specified.
+
+  On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM, 
+  and VLC_EGENERIC for some other error.
+ *****************************************************************************/
+static int 
+VCDOpen( vlc_object_t *p_this )
+{
+    input_thread_t *        p_input = (input_thread_t *)p_this;
+    thread_vcd_data_t *     p_vcd;
+    char *                  psz_source;
+    vcdinfo_itemid_t        itemid;
+    bool                    play_ok;
+    
+    p_input->pf_read        = VCDRead;
+    p_input->pf_seek        = VCDSeek;
+    p_input->pf_set_area    = VCDSetArea;
+    p_input->pf_set_program = VCDSetProgram;
+
+    p_vcd = malloc( sizeof(thread_vcd_data_t) );
+
+    if( p_vcd == NULL )
+    {
+        LOG_ERR ("out of memory" );
+        return VLC_ENOMEM;
+    }
+
+    p_input->p_access_data = (void *)p_vcd;
+    p_vcd->i_debug         = config_GetInt( p_this, MODULE_STRING "-debug" );
+    psz_source             = VCDParse( p_input, &itemid );
+
+    if ( NULL == psz_source ) 
+    {
+      free( p_vcd );
+      return( VLC_EGENERIC );
+    }
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "%s", psz_source );
+
+    p_vcd->p_segments = NULL;
+    p_vcd->p_entries  = NULL;
+    
+    /* set up input  */
+    p_input->i_mtu = VCD_DATA_ONCE;
+
+    vlc_mutex_lock( &p_input->stream.stream_lock );
+
+    /* If we are here we can control the pace... */
+    p_input->stream.b_pace_control = 1;
+
+    p_input->stream.b_seekable = 1;
+    p_input->stream.p_selected_area->i_size = 0;
+    p_input->stream.p_selected_area->i_tell = 0;
+
+    vlc_mutex_unlock( &p_input->stream.stream_lock );
+
+    if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
+    {
+        msg_Warn( p_input, "could not open %s", psz_source );
+        free( psz_source );
+        free( p_vcd );
+        return VLC_EGENERIC;
+    }
+
+    /* Get track information. */
+    p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
+                                            vcdinfo_get_cd_image(p_vcd->vcd), 
+                                            &p_vcd->p_sectors );
+    free( psz_source );
+    if( p_vcd->num_tracks < 0 )
+        LOG_ERR ("unable to count tracks" );
+    else if( p_vcd->num_tracks <= 1 )
+        LOG_ERR ("no movie tracks found" );
+    if( p_vcd->num_tracks <= 1)
+    {
+        vcdinfo_close( p_vcd->vcd );
+        free( p_vcd );
+        return VLC_EGENERIC;
+    }
+
+    /* Set stream and area data */
+    vlc_mutex_lock( &p_input->stream.stream_lock );
+
+    /* Initialize ES structures */
+    input_InitStream( p_input, sizeof( stream_ps_data_t ) );
+
+    /* disc input method */
+    p_input->stream.i_method = INPUT_METHOD_VCD;
+
+    p_input->stream.i_area_nb = 1;
+    
+
+    /* Initialize segment information. */
+    VCDSegments( p_input );
+    
+    /* Initialize track area information. */
+    VCDTracks( p_input );
+    
+    if( VCDEntryPoints( p_input ) < 0 )
+    {
+        msg_Warn( p_input, "could not read entry points, will not use them" );
+        p_vcd->b_valid_ep = false;
+    }
+
+    if( VCDLIDs( p_input ) < 0 )
+    {
+        msg_Warn( p_input, "could not read entry LIDs" );
+    }
+
+    play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
+    
+    vlc_mutex_unlock( &p_input->stream.stream_lock );
+
+    if ( ! play_ok ) {
+      vcdinfo_close( p_vcd->vcd );
+      free( p_vcd );
+      return VLC_EGENERIC;
+    }
+
+    if( !p_input->psz_demux || !*p_input->psz_demux )
+    {
+#if FIXED
+      p_input->psz_demux = "vcdx";
+#else
+      p_input->psz_demux = "ps";
+#endif
+    }
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * VCDClose: closes VCD releasing allocated memory.
+ *****************************************************************************/
+static void 
+VCDClose( vlc_object_t *p_this )
+{
+    input_thread_t *   p_input = (input_thread_t *)p_this;
+    thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
+    vcdinfo_close( p_vcd->vcd );
+
+    free( p_vcd->p_entries );
+    free( p_vcd->p_segments );
+    free( p_vcd );
+    p_vcd_input = NULL;
+}
+
+/*****************************************************************************
+ * VCDRead: reads i_len bytes from the VCD into p_buffer.
+ *****************************************************************************
+ * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
+ * bytes.
+ *****************************************************************************/
+static int 
+VCDRead( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
+{
+    thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
+    int                     i_blocks;
+    int                     i_index;
+    int                     i_read;
+    byte_t                  p_last_sector[ M2F2_SECTOR_SIZE ];
+
+    i_read = 0;
+
+    /* Compute the number of blocks we have to read */
+
+    i_blocks = i_len / M2F2_SECTOR_SIZE;
+
+    for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
+    {
+
+      if ( p_vcd->cur_lsn == p_vcd->end_lsn ) {
+        vcdplayer_read_status_t read_status;
+
+        /* We've run off of the end of this entry. Do we continue or stop? */
+        dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
+                   "end reached, cur: %u", p_vcd->cur_lsn );
+
+        read_status = vcdplayer_pbc_is_on( p_vcd ) 
+          ? vcdplayer_pbc_nav( p_input ) 
+          : vcdplayer_non_pbc_nav( p_input );
+
+        switch (read_status) {
+        case READ_END:
+          /* End reached. Return NULL to indicated this. */
+        case READ_ERROR:
+          /* Some sort of error. */
+          return i_read;
+        case READ_STILL_FRAME: 
+          {
+            byte_t * p_buf = p_buffer;
+            p_buf += (i_index*M2F2_SECTOR_SIZE);
+            memset(p_buf, 0, M2F2_SECTOR_SIZE);
+            p_buf += 2;
+            *p_buf = 0x01;
+            dbg_print(INPUT_DBG_STILL, "Handled still event\n");
+            return i_read + M2F2_SECTOR_SIZE;
+          }
+        default:
+        case READ_BLOCK:
+          break;
+        }
+      }
+
+      if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
+                          p_vcd->cur_lsn, 
+                          p_buffer + (i_index*M2F2_SECTOR_SIZE) ) < 0 )
+        {
+          LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
+          return -1;
+        }
+      
+      p_vcd->cur_lsn ++;
+      
+      /* Update chapter */
+      if( p_vcd->b_valid_ep &&
+          /* FIXME kludge so that read does not update chapter
+           * when a manual chapter change was requested and not
+           * yet accomplished */
+          !p_input->stream.p_new_area )
+        {
+          unsigned int i_entry = p_input->stream.p_selected_area->i_part;
+          
+          vlc_mutex_lock( &p_input->stream.stream_lock );
+          
+          if( i_entry < p_vcd->num_entries &&
+              p_vcd->cur_lsn >= p_vcd->p_entries[i_entry+1] )
+            {
+              dbg_print( INPUT_DBG_PBC, 
+                         "new entry, i_entry %d, sector %d, es %d",
+                         i_entry, p_vcd->cur_lsn, 
+                         p_vcd->p_entries[i_entry] );
+              p_vcd->play_item.num = 
+                ++ p_input->stream.p_selected_area->i_part;
+              p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
+              VCDUpdateVar( p_input, p_vcd->play_item.num, VLC_VAR_SETVALUE,
+                            "chapter", "Setting entry" );
+            }
+          vlc_mutex_unlock( &p_input->stream.stream_lock );
+        }
+
+        i_read += M2F2_SECTOR_SIZE;
+    }
+
+    if ( i_len % M2F2_SECTOR_SIZE ) /* this should not happen */
+    {
+        if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
+                            p_vcd->cur_lsn, p_last_sector ) < 0 )
+        {
+            LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
+            return -1;
+        }
+
+        p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * M2F2_SECTOR_SIZE,
+                                   p_last_sector, i_len % M2F2_SECTOR_SIZE );
+        i_read += i_len % M2F2_SECTOR_SIZE;
+    }
+
+    return i_read;
+}
+
+
+/*****************************************************************************
+ * VCDSetProgram: Does nothing since a VCD is mono_program
+ *****************************************************************************/
+static int 
+VCDSetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_program)
+{
+    thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDSetProgram" );
+    return 0;
+}
+
+
+/*****************************************************************************
+ * VCDSetArea: initialize internal data structures and input stream data 
+   so set subsequent reading and seeking to reflect that we are
+   at track x, entry or segment y.
+   This is called for each user navigation request, e.g. the GUI 
+   Chapter/Title selections or in initial MRL parsing. 
+ ****************************************************************************/
+int 
+VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
+{
+    thread_vcd_data_t *p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
+    unsigned int i_entry = p_area->i_part;
+    track_t i_track      = p_area->i_id;
+    int old_seekable     = p_input->stream.b_seekable;
+    unsigned int i_nb    = p_area->i_plugin_data + p_area->i_part_nb;
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT),
+               "track: %d, entry %d, seekable %d",
+               i_track, i_entry, old_seekable );
+
+    /* we can't use the interface slider until initilization is complete */
+    p_input->stream.b_seekable = 0;
+
+    if( p_area != p_input->stream.p_selected_area )
+    {
+        unsigned int i;
+
+        /* If is the result of a track change, make the entry valid. */
+        if (i_entry < p_area->i_plugin_data || i_entry >= i_nb) 
+          i_entry = p_area->i_plugin_data;
+
+        /* Change the default area */
+        p_input->stream.p_selected_area = p_area;
+
+        /* Update the navigation variables without triggering a callback */
+        VCDUpdateVar( p_input, i_track, VLC_VAR_SETVALUE, "title", 
+                      "Setting track");
+
+        var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
+        for( i = p_area->i_plugin_data; i < i_nb; i++ )
+        {
+          VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE, 
+                        "chapter",  "Adding entry choice");
+        }
+    }
+
+    if (i_track == 0) 
+      VCDSetOrigin( p_input, p_vcd->p_segments[i_entry], 
+                    p_vcd->p_segments[i_entry], p_vcd->p_segments[i_entry+1],
+                    i_entry, 0 );
+    else
+      VCDSetOrigin( p_input, p_vcd->p_sectors[i_track], 
+                    vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry), 
+                    p_vcd->p_sectors[i_track+1],
+                    i_entry, i_track );
+
+    p_input->stream.b_seekable = old_seekable;
+    /* warn interface that something has changed */
+    p_input->stream.b_changed = 1;
+
+    return VLC_SUCCESS;
+}
+
+
+/****************************************************************************
+ * VCDSeek
+ ****************************************************************************/
+void 
+VCDSeek( input_thread_t * p_input, off_t i_off )
+{
+    thread_vcd_data_t * p_vcd;
+    unsigned int i_entry=0; /* invalid entry */
+
+    p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
+
+    p_vcd->cur_lsn = p_vcd->origin_lsn + (i_off / (off_t)M2F2_SECTOR_SIZE);
+
+    vlc_mutex_lock( &p_input->stream.stream_lock );
+#define p_area p_input->stream.p_selected_area
+    /* Find entry */
+    if( p_vcd->b_valid_ep )
+    {
+        for( i_entry = 1 ; i_entry < p_vcd->num_entries ; i_entry ++ )
+        {
+            if( p_vcd->cur_lsn < p_vcd->p_entries[i_entry] )
+            {
+              VCDUpdateVar( p_input, i_entry, VLC_VAR_SETVALUE, 
+                            "chapter", "Setting entry" );
+              break;
+            }
+        }
+        p_vcd->play_item.num  = p_area->i_part = i_entry;
+        p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
+    }
+#undef p_area
+
+    p_input->stream.p_selected_area->i_tell = i_off;
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
+    "orig %d, cur: %d, offset: %lld, start: %lld, entry %d", 
+               p_vcd->origin_lsn, p_vcd->cur_lsn, i_off, 
+               p_input->stream.p_selected_area->i_start, i_entry );
+
+    vlc_mutex_unlock( &p_input->stream.stream_lock );
+}
+
+/*****************************************************************************
+  VCDPlay: set up internal structures so seeking/reading places an item.
+  itemid: the thing to play.
+  user_entry: true if itemid is a user selection (rather than internally-
+  generated selection such as via PBC) in which case we may have to adjust 
+  for differences in numbering. 
+ *****************************************************************************/
+int
+VCDPlay( input_thread_t *p_input, vcdinfo_itemid_t itemid )
+{
+    thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
+    input_area_t *          p_area;
+    
+    p_vcd->in_still = 0;
+
+#define area p_input->stream.pp_areas
+
+    switch (itemid.type) {
+    case VCDINFO_ITEM_TYPE_TRACK:
+
+      /* Valid tracks go from 1...num_tracks-1, because track 0 is unplayable.
+       */
+
+      if (itemid.num == 0 || itemid.num >= p_vcd->num_tracks) {
+        LOG_ERR ("Invalid track number %d", itemid.num );
+        return VLC_EGENERIC;
+      }
+      p_area           = area[itemid.num];
+      p_area->i_part   = p_area->i_plugin_data;
+      p_input->stream.b_seekable = 1;
+      break;
+    case VCDINFO_ITEM_TYPE_SEGMENT: 
+      /* Valid segments go from 0...num_segments-1. */
+      if (itemid.num >= p_vcd->num_segments) {
+        LOG_ERR ( "Invalid segment number: %d", itemid.num );
+        return VLC_EGENERIC;
+      } else {
+        vcdinfo_video_segment_type_t segtype = 
+          vcdinfo_get_video_type(p_vcd->vcd, itemid.num);
+        
+        dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d", 
+                  vcdinfo_video_type2str(p_vcd->vcd, itemid.num), 
+                  (int) segtype, itemid.num);
+        
+        p_area           = area[0];
+        p_area->i_part   = itemid.num;
+        
+        switch (segtype)
+          {
+          case VCDINFO_FILES_VIDEO_NTSC_STILL:
+          case VCDINFO_FILES_VIDEO_NTSC_STILL2:
+          case VCDINFO_FILES_VIDEO_PAL_STILL:
+          case VCDINFO_FILES_VIDEO_PAL_STILL2:
+            p_input->stream.b_seekable = 0;
+            p_vcd->in_still = -5;
+            break;
+          default:
+            p_input->stream.b_seekable = 1;
+            p_vcd->in_still = 0;
+          }
+      }
+      break;
+      
+    case VCDINFO_ITEM_TYPE_LID:
+      /* LIDs go from 1..num_lids. */
+      if (itemid.num == 0 || itemid.num > p_vcd->num_lids) {
+        LOG_ERR ( "Invalid LID number: %d", itemid.num );
+        return VLC_EGENERIC;
+      } else {
+        p_vcd->cur_lid = itemid.num;
+        vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
+    
+        switch (p_vcd->pxd.descriptor_type) {
+      
+        case PSD_TYPE_SELECTION_LIST:
+        case PSD_TYPE_EXT_SELECTION_LIST: {
+          vcdinfo_itemid_t trans_itemid;
+          uint16_t trans_itemid_num;
+          
+          if (p_vcd->pxd.psd == NULL) return VLC_EGENERIC;
+          trans_itemid_num  = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
+          vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
+          p_vcd->loop_count = 1;
+          p_vcd->loop_item  = trans_itemid;
+          return VCDPlay( p_input, trans_itemid );
+          break;
+        }
+          
+        case PSD_TYPE_PLAY_LIST: {
+          if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
+          p_vcd->pdi = -1;
+          return vcdplayer_inc_play_item(p_input) 
+            ? VLC_SUCCESS : VLC_EGENERIC;
+          break;
+        }
+          
+        case PSD_TYPE_END_LIST:
+        case PSD_TYPE_COMMAND_LIST:
+          
+        default:
+          ;
+        }
+      }
+      return VLC_EGENERIC;
+    case VCDINFO_ITEM_TYPE_ENTRY:
+      /* Entries go from 0..num_entries-1. */
+      if (itemid.num >= p_vcd->num_entries) {
+        LOG_ERR ("Invalid entry number: %d", itemid.num );
+        return VLC_EGENERIC;
+      } else {
+        track_t cur_track  = vcdinfo_get_track(p_vcd->vcd,  itemid.num);
+        p_area             = area[cur_track];
+        p_area->i_part     = itemid.num;
+        p_input->stream.b_seekable = 1;
+      }
+      break;
+    default:
+      LOG_ERR ("unknown entry type" );
+      return VLC_EGENERIC;
+    }
+
+    VCDSetArea( p_input, p_area );
+
+#undef area
+
+    p_vcd->play_item = itemid;
+
+    dbg_print( (INPUT_DBG_CALL), 
+               "i_start %lld, i_size: %lld, i_tell: %lld, lsn %d", 
+               p_area->i_start, p_area->i_size, 
+               p_area->i_tell, p_vcd->cur_lsn );
+        
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+  VCDEntryPoints: Reads the information about the entry points on the disc
+  and initializes area information with that.
+  Before calling this track information should have been read in.
+ *****************************************************************************/
+static int 
+VCDEntryPoints( input_thread_t * p_input )
+{
+    thread_vcd_data_t *               p_vcd;
+    unsigned int                      i_nb;
+    unsigned int                      i, i_entry_index = 0;
+    unsigned int                      i_previous_track = CDIO_INVALID_TRACK;
+
+    p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
+
+    i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
+    if (0 == i_nb) 
+      return -1;
+    
+    p_vcd->p_entries  = malloc( sizeof( lba_t ) * i_nb );
+
+    if( p_vcd->p_entries == NULL )
+    {
+        LOG_ERR ("not enough memory for entry points treatment" );
+        return -1;
+    }
+
+    p_vcd->num_entries = 0;
+
+    for( i = 0 ; i < i_nb ; i++ )
+    {
+        track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
+        if( i_track <= p_input->stream.i_area_nb )
+        {
+            p_vcd->p_entries[i] = 
+              vcdinfo_get_entry_lsn(p_vcd->vcd, i);
+            p_input->stream.pp_areas[i_track]->i_part_nb ++;
+
+            /* if this entry belongs to a new track */
+            if( i_track != i_previous_track )
+            {
+                /* i_plugin_data is used to store the first entry of the area*/
+                p_input->stream.pp_areas[i_track]->i_plugin_data =
+                                                            i_entry_index;
+                i_previous_track = i_track;
+                p_input->stream.pp_areas[i_track]->i_part_nb = 1;
+            }
+            i_entry_index ++;
+            p_vcd->num_entries ++;
+        }
+        else
+            msg_Warn( p_input, "wrong track number found in entry points" );
+    }
+    p_vcd->b_valid_ep = true;
+    return 0;
+}
+
+/*****************************************************************************
+ * VCDSegments: Reads the information about the segments the disc.
+ *****************************************************************************/
+static int
+VCDSegments( input_thread_t * p_input )
+{
+    thread_vcd_data_t * p_vcd;
+    unsigned int        i;
+    unsigned int        num_segments;
+    
+
+    p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
+    num_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
+
+#define area p_input->stream.pp_areas
+
+    /* area 0 is reserved for segments. Set Absolute start offset
+         and size */
+    area[0]->i_plugin_data = 0;
+    input_DelArea( p_input, area[0] );
+    input_AddArea( p_input, 0, 0 );
+    
+    area[0]->i_start = (off_t)p_vcd->p_sectors[0] 
+      * (off_t)M2F2_SECTOR_SIZE;
+    area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
+      * (off_t)M2F2_SECTOR_SIZE;
+    
+    /* Default Segment  */
+    area[0]->i_part = 0;
+    
+    /* i_plugin_data is used to store which entry point is the first
+       of the track (area) */
+    area[0]->i_plugin_data = 0;
+
+    area[0]->i_part_nb = 0;
+    
+    dbg_print( INPUT_DBG_MRL, "area id %d, for segment track %d", 
+               area[0]->i_id, 0 );
+
+    if (num_segments == 0) return 0;
+
+    /* We have one additional segment allocated so we can get the size
+       by subtracting seg[i+1] - seg[i].
+     */
+    p_vcd->p_segments = malloc( sizeof( lba_t ) * (num_segments+1) );
+    if( p_vcd->p_segments == NULL )
+    {
+        LOG_ERR ("not enough memory for segment treatment" );
+        return -1;
+    }
+
+    /* Update the navigation variables without triggering a callback */
+    VCDUpdateVar( p_input, 0, VLC_VAR_SETVALUE, "title", "Setting track" );
+    var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
+    
+    for( i = 0 ; i < num_segments ; i++ )
+    {
+      p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
+      area[0]->i_part_nb ++;
+      VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE, 
+                    "chapter", "Adding segment choice");
+    }
+
+#undef area
+
+    p_vcd->p_segments[num_segments] = p_vcd->p_segments[num_segments-1]+
+      vcdinfo_get_seg_sector_count(p_vcd->vcd, num_segments-1);
+    
+    return 0;
+}
+
+/*****************************************************************************
+ VCDTracks: initializes area information. 
+ Before calling this track information should have been read in.
+ *****************************************************************************/
+static void
+VCDTracks( input_thread_t * p_input )
+{
+    thread_vcd_data_t * p_vcd;
+    unsigned int        i;
+
+    p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
+
+#define area p_input->stream.pp_areas
+
+    /* We start area addressing for tracks at 1 since the default area 0
+       is reserved for segments */
+
+    for( i = 1 ; i < p_vcd->num_tracks ; i++ )
+    {
+        /* Tracks are Program Chains */
+        input_AddArea( p_input, i, i );
+
+        /* Absolute start byte offset and byte size */
+        area[i]->i_start = (off_t) p_vcd->p_sectors[i] 
+                           * (off_t)M2F2_SECTOR_SIZE;
+        area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
+                           * (off_t)M2F2_SECTOR_SIZE;
+
+        /* Current entry being played in track */
+        area[i]->i_part = 0;
+
+        /* i_plugin_data is used to store which entry point is the first
+         * of the track (area) */
+        area[i]->i_plugin_data = 0;
+
+        dbg_print( INPUT_DBG_MRL, 
+                   "area[%d] id: %d, i_start: %lld, i_size: %lld", 
+                   i, area[i]->i_id, area[i]->i_start, area[i]->i_size );
+    }
+
+#undef area
+
+    return ;
+}
+
+/*****************************************************************************
+  VCDLIDs: Reads the LIST IDs from the LOT.
+ *****************************************************************************/
+static int 
+VCDLIDs( input_thread_t * p_input )
+{
+    thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
+
+    p_vcd->num_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
+    p_vcd->cur_lid  = VCDINFO_INVALID_ENTRY;
+
+    if (vcdinfo_read_psd (p_vcd->vcd)) {
+      
+      vcdinfo_visit_lot (p_vcd->vcd, false);
+      
+      if (vcdinfo_get_psd_x_size(p_vcd->vcd))
+        vcdinfo_visit_lot (p_vcd->vcd, true);
+    }
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL), 
+               "num LIDs=%d", p_vcd->num_lids);
+
+    return 0;
+}
+
+/*****************************************************************************
+ * VCDParse: parse command line
+ *****************************************************************************/
+static char * 
+VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid )
+{
+    thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
+    char *             psz_parser;
+    char *             psz_source;
+    char *             psz_next;
+
+    p_itemid->type=VCDINFO_ITEM_TYPE_TRACK;
+    p_itemid->num=1;
+    
+#ifdef WIN32
+    /* On Win32 we want the VCD access plugin to be explicitly requested,
+     * we end up with lots of problems otherwise */
+    if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
+#endif
+
+    if( !p_input->psz_name )
+    {
+        return NULL;
+    }
+
+    psz_parser = psz_source = strdup( p_input->psz_name );
+
+    /* Parse input string :
+     * [device][@[type][title]] */
+    while( *psz_parser && *psz_parser != '@' )
+    {
+        psz_parser++;
+    }
+
+    if( *psz_parser == '@' )
+    {
+      /* Found the divide between the source name and the 
+         type+entry number. */
+      unsigned int num;
+      
+      *psz_parser = '\0';
+      ++psz_parser;
+      if( *psz_parser )
+        {
+          switch(*psz_parser) {
+          case 'E': 
+            p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
+            ++psz_parser;
+            break;
+          case 'P': 
+            p_itemid->type = VCDINFO_ITEM_TYPE_LID;
+            ++psz_parser;
+            break;
+          case 'S': 
+            p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
+            ++psz_parser;
+            break;
+          case 'T': 
+            p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
+            ++psz_parser;
+            break;
+          default: ;
+          }
+        }
+      
+      num = strtol( psz_parser, &psz_next, 10 );
+      if ( *psz_parser != '\0' && *psz_next == '\0') 
+        {
+          p_itemid->num = num;
+        }
+      
+    }
+
+    if( !*psz_source )
+    {
+        if( !p_input->psz_access )
+        {
+            return NULL;
+        }
+        psz_source = config_GetPsz( p_input, "vcd" );
+        if( !psz_source ) return NULL;
+    }
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL), 
+               "source=%s entry=%d type=%d",
+               psz_source, p_itemid->num, p_itemid->type);
+
+    return psz_source;
+}
+
+/* 
+   Set's start origin subsequent seeks/reads
+*/
+static void 
+VCDSetOrigin( input_thread_t *p_input, lsn_t origin_lsn, 
+              lsn_t cur_lsn, lsn_t end_lsn, int cur_entry, track_t cur_track )
+{
+  thread_vcd_data_t * p_vcd  = (thread_vcd_data_t *) p_input->p_access_data;
+
+  p_vcd->origin_lsn = origin_lsn;
+  p_vcd->cur_lsn    = cur_lsn;
+  p_vcd->end_lsn    = end_lsn;
+  p_vcd->cur_track  = cur_track;
+  p_vcd->play_item.num  = cur_entry;
+  p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
+  
+  dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
+             "origin: %d, cur_lsn: %d, end_lsn: %d, entry: %d, track: %d",
+             origin_lsn, cur_lsn, end_lsn, cur_entry, cur_track );
+
+  p_input->stream.p_selected_area->i_tell =
+    (off_t) (p_vcd->cur_lsn - p_vcd->origin_lsn) * (off_t)M2F2_SECTOR_SIZE;
+
+  VCDUpdateVar( p_input, cur_entry, VLC_VAR_SETVALUE, 
+                "chapter", "Setting entry");
+}
+
+/*****************************************************************************
+ * vcd_Open: Opens a VCD device or file and returns an opaque handle
+ *****************************************************************************/
+static vcdinfo_obj_t *
+vcd_Open( vlc_object_t *p_this, const char *psz_dev )
+{
+    vcdinfo_obj_t *p_vcdobj;
+    char  *actual_dev;
+
+    if( !psz_dev ) return NULL;
+
+    /* Set where to log errors messages from libcdio. */
+    p_vcd_input = (input_thread_t *)p_this;
+    cdio_log_set_handler ( cdio_log_handler );
+    vcd_log_set_handler ( vcd_log_handler );
+    
+    actual_dev=strdup(psz_dev);
+    if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) != 
+         VCDINFO_OPEN_VCD) {
+      free(actual_dev);
+      return NULL;
+    }
+    free(actual_dev);
+
+    return p_vcdobj;
+}
+
+/****************************************************************************
+ * VCDReadSector: Read a sector (2324 bytes)
+ ****************************************************************************/
+static int 
+VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
+               lsn_t cur_lsn, byte_t * p_buffer )
+{
+  typedef struct {
+    uint8_t subheader   [8];
+    uint8_t data        [M2F2_SECTOR_SIZE];
+  } vcdsector_t;
+  vcdsector_t vcd_sector;
+  
+  if (cdio_read_mode2_sector(vcdinfo_get_cd_image(p_vcd), 
+                             &vcd_sector, cur_lsn, true) 
+      != 0)
+    {
+      msg_Warn( p_this, "Could not read LSN %d", cur_lsn );
+      return -1;
+    }
+    
+  memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
+  
+  return( 0 );
+}
+
+/****************************************************************************
+ Update the "varname" variable to i_num without triggering a callback.
+****************************************************************************/
+static void
+VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
+              const char *varname, const char *label)
+{
+  vlc_value_t val;
+  val.i_int = i_num;
+  if (NULL != p_vcd_input) {
+    thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
+    dbg_print( INPUT_DBG_PBC, "%s %d", label, i_num );
+  }
+  var_Change( p_input, varname, i_action, &val, NULL );
+}
+#endif
diff --git a/modules/access/vcdx/cdrom.c b/modules/access/vcdx/cdrom.c
new file mode 100644 (file)
index 0000000..95cf42e
--- /dev/null
@@ -0,0 +1,158 @@
+/****************************************************************************
+ * cdrom.c: cdrom tools
+ *****************************************************************************
+ * Copyright (C) 1998-2001 VideoLAN
+ * $Id: cdrom.c,v 1.1 2003/10/04 18:55:13 gbazin Exp $
+ *
+ * Authors: Johan Bilien <jobi@via.ecp.fr>
+ *          Gildas Bazin <gbazin@netcourrier.com>
+ *          Jon Lech Johansen <jon-vl@nanocrew.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <vlc/vlc.h>
+
+#ifdef HAVE_UNISTD_H
+#   include <unistd.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+
+#include "cdrom.h"
+
+/*****************************************************************************
+ * Local Prototypes
+ *****************************************************************************/
+static void cd_log_handler (cdio_log_level_t level, const char message[]);
+
+/*****************************************************************************
+ * ioctl_Open: Opens a VCD device or file and returns an opaque handle
+ *****************************************************************************/
+cddev_t *ioctl_Open( vlc_object_t *p_this, const char *psz_dev )
+{
+    cddev_t *p_cddev;
+
+    if( !psz_dev ) return NULL;
+
+    /*
+     *  Initialize structure with default values
+     */
+    p_cddev = (cddev_t *)malloc( sizeof(cddev_t) );
+    if( p_cddev == NULL )
+    {
+        msg_Err( p_this, "out of memory" );
+        return NULL;
+    }
+
+    /* Set where to log errors messages from libcdio. */
+    cdio_log_set_handler ( cd_log_handler );
+
+    p_cddev->cdio = cdio_open(psz_dev, DRIVER_UNKNOWN);
+
+    if( p_cddev->cdio == NULL )
+    {
+        free( p_cddev );
+        p_cddev = NULL;
+    }
+
+    return p_cddev;
+}
+
+/*****************************************************************************
+ * ioctl_Close: Closes an already opened VCD device or file.
+ *****************************************************************************/
+void ioctl_Close( cddev_t *p_cddev )
+{
+    cdio_destroy(p_cddev->cdio);
+}
+
+/*****************************************************************************
+ * ioctl_GetTracksMap: Read the Table of Contents, fill in the pp_sectors map
+ *                     if pp_sectors is not null and return the number of
+ *                     tracks available.
+ *                     We allocate and fill one more track than are on 
+ *                     the CD. The last "track" is leadout track information.
+ *                     This makes finding the end of the last track uniform
+ *                     how it is done for other tracks.
+ *****************************************************************************/
+track_t ioctl_GetTracksMap( vlc_object_t *p_this, const CdIo *cdio,
+                            lsn_t **pp_sectors )
+{
+    track_t i_tracks     = cdio_get_num_tracks(cdio);
+    track_t first_track  = cdio_get_first_track_num(cdio);
+    track_t i;
+
+
+    *pp_sectors = malloc( (i_tracks + 1) * sizeof(lsn_t) );
+    if( *pp_sectors == NULL )
+      {
+        msg_Err( p_this, "out of memory" );
+        return 0;
+      }
+
+    /* Fill the p_sectors structure with the track/sector matches.
+       Note cdio_get_track_lsn when given num_tracks + 1 will return
+       the leadout LSN.
+     */
+    for( i = 0 ; i <= i_tracks ; i++ )
+      {
+        (*pp_sectors)[ i ] = cdio_get_track_lsn(cdio, first_track+i);
+      }
+    
+    return i_tracks;
+}
+
+/****************************************************************************
+ * ioctl_ReadSector: Read a sector (2324 bytes)
+ ****************************************************************************/
+int ioctl_ReadSector( vlc_object_t *p_this, const cddev_t *p_cddev,
+                      int i_sector, byte_t * p_buffer )
+{
+  typedef struct {
+    uint8_t subheader   [8];
+    uint8_t data        [M2F2_SECTOR_SIZE];
+  } vcdsector_t;
+  vcdsector_t vcd_sector;
+  
+  if (cdio_read_mode2_sector(p_cddev->cdio, &vcd_sector, i_sector, true) 
+      != 0)
+    {
+      // msg_Err( p_this, "Could not read sector %d", i_sector );
+      return -1;
+    }
+    
+  memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
+  
+  return( 0 );
+}
+
+/****************************************************************************
+ * Private functions
+ ****************************************************************************/
+
+/* For now we're going to just discard error messages from libcdio... */
+static void
+cd_log_handler (cdio_log_level_t level, const char message[])
+{
+  return;
+}
diff --git a/modules/access/vcdx/cdrom.h b/modules/access/vcdx/cdrom.h
new file mode 100644 (file)
index 0000000..9c198af
--- /dev/null
@@ -0,0 +1,45 @@
+/****************************************************************************
+ * cdrom.h: cdrom tools header
+ *****************************************************************************
+ * Copyright (C) 1998-2001 VideoLAN
+ * $Id: cdrom.h,v 1.1 2003/10/04 18:55:13 gbazin Exp $
+ *
+ * Authors: Johan Bilien <jobi@via.ecp.fr>
+ *          Gildas Bazin <gbazin@netcourrier.com>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+#include <cdio/cdio.h>
+#include <cdio/logging.h>
+
+/*****************************************************************************
+ * The cddev structure
+ *****************************************************************************/
+typedef struct cddev_s
+{
+    int    *p_sectors;                           /* tracks layout on the vcd */
+    CdIo  *cdio;                                /* libcdio uses this to read */
+
+} cddev_t;
+
+/*****************************************************************************
+ * Prototypes
+ *****************************************************************************/
+cddev_t  *ioctl_Open         ( vlc_object_t *, const char * );
+void      ioctl_Close        ( cddev_t * );
+track_t   ioctl_GetTracksMap ( vlc_object_t *, const CdIo *, lsn_t ** );
+int       ioctl_ReadSector   ( vlc_object_t *, const cddev_t *,
+                               int, byte_t * );
diff --git a/modules/access/vcdx/demux.c b/modules/access/vcdx/demux.c
new file mode 100644 (file)
index 0000000..1c23f87
--- /dev/null
@@ -0,0 +1,195 @@
+/*****************************************************************************
+ * demux.c: demux functions for dvdplay.
+ *****************************************************************************
+ * Copyright (C) 1998-2001 VideoLAN
+ * $Id: demux.c,v 1.1 2003/10/04 18:55:13 gbazin Exp $
+ *
+ * Author: Stéphane Borel <stef@via.ecp.fr>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <vlc/vlc.h>
+#include <vlc/input.h>
+#include <vlc/intf.h>
+
+#include "../../demux/mpeg/system.h"
+
+#ifdef HAVE_UNISTD_H
+#   include <unistd.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef STRNCASECMP_IN_STRINGS_H
+#   include <strings.h>
+#endif
+
+#include "vcd.h"
+#include "intf.h"
+
+/* how many packets vcdx_Demux will read in each loop */
+/* #define vcdplay_READ_ONCE 64 */
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int  Demux         ( input_thread_t * );
+
+/*****************************************************************************
+ * Private structure
+ *****************************************************************************/
+struct demux_sys_t
+{
+    vcd_data_t * p_vcd;
+
+    module_t *   p_module;
+    mpeg_demux_t mpeg;
+};
+
+/*****************************************************************************
+ * InitVCD: initializes structures
+ *****************************************************************************/
+int E_(InitVCD) ( vlc_object_t *p_this )
+{
+    input_thread_t *p_input = (input_thread_t *)p_this;
+    vcd_data_t *    p_vcd = (vcd_data_t *)p_input->p_access_data;
+    demux_sys_t *   p_demux;
+
+    printf("++++ InitVCD CALLED\n");
+    
+
+    if( p_input->stream.i_method != INPUT_METHOD_VCD )
+    {
+        return VLC_EGENERIC;
+    }
+
+    p_demux = p_input->p_demux_data = malloc( sizeof(demux_sys_t ) );
+    if( p_demux == NULL )
+    {
+        return VLC_ENOMEM;
+    }
+
+    p_input->p_private = (void*)&p_demux->mpeg;
+    p_demux->p_module = module_Need( p_input, "mpeg-system", NULL );
+    if( p_demux->p_module == NULL )
+    {
+        free( p_input->p_demux_data );
+        return VLC_ENOMOD;
+    }
+
+    p_input->p_demux_data->p_vcd = p_vcd;
+
+    p_input->pf_demux = Demux;
+    p_input->pf_rewind = NULL;
+
+    p_vcd->p_intf = NULL;
+    p_vcd->i_still_time = 0;
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * EndVCD: frees unused data
+ *****************************************************************************/
+void E_(EndVCD) ( vlc_object_t *p_this )
+{
+    input_thread_t *p_input = (input_thread_t *)p_this;
+    vcd_data_t *    p_vcd = p_input->p_demux_data->p_vcd;
+    intf_thread_t * p_intf = NULL;
+
+    p_intf = vlc_object_find( p_input, VLC_OBJECT_INTF, FIND_CHILD );
+    if( p_intf != NULL )
+    {
+        intf_StopThread( p_intf );
+        vlc_object_detach( p_intf );
+        vlc_object_release( p_intf );
+        intf_Destroy( p_intf );
+    }
+
+    p_vcd->p_intf = NULL;
+
+    module_Unneed( p_input, p_input->p_demux_data->p_module );
+    free( p_input->p_demux_data );
+}
+
+/*****************************************************************************
+ * Demux
+ *****************************************************************************/
+static int Demux( input_thread_t * p_input )
+{
+    vcd_data_t *            p_vcd;
+    data_packet_t *         p_data;
+    ssize_t                 i_result;
+    ptrdiff_t               i_remains;
+    int                     i_data_nb = 0;
+
+    p_vcd = p_input->p_demux_data->p_vcd;
+
+    /* Read headers to compute payload length */
+    do
+    {
+        i_result = p_input->p_demux_data->mpeg.pf_read_ps( p_input, &p_data );
+
+        if( i_result <= 0 )
+        {
+            return i_result;
+        }
+
+        i_remains = p_input->p_last_data - p_input->p_current_data;
+
+        p_input->p_demux_data->mpeg.pf_demux_ps( p_input, p_data );
+
+
+        ++i_data_nb;
+    }
+    while( i_remains );
+
+
+
+//    if( p_vcd->b_still && p_vcd->b_end_of_cell && p_vcd->p_intf != NULL )
+    if( p_vcd->i_still_time && p_vcd->b_end_of_cell && p_vcd->p_intf != NULL )
+    {
+        pgrm_descriptor_t * p_pgrm;
+
+        /* when we receive still_time flag, we have to pause immediately */
+        input_SetStatus( p_input, INPUT_STATUS_PAUSE );
+
+        vcdIntfStillTime( p_vcd->p_intf, p_vcd->i_still_time );
+        p_vcd->i_still_time = 0;
+
+        vlc_mutex_lock( &p_input->stream.stream_lock );
+
+        p_pgrm = p_input->stream.p_selected_program;
+        p_pgrm->i_synchro_state = SYNCHRO_REINIT;
+
+        vlc_mutex_unlock( &p_input->stream.stream_lock );
+
+        input_ClockManageControl( p_input, p_pgrm, 0 );
+    }
+
+    return i_data_nb;
+}
diff --git a/modules/access/vcdx/intf.c b/modules/access/vcdx/intf.c
new file mode 100644 (file)
index 0000000..cf309e2
--- /dev/null
@@ -0,0 +1,276 @@
+/*****************************************************************************
+ * intf.c: Video CD interface to handle user interaction and still time
+ *****************************************************************************
+ * Copyright (C) 2002 VideoLAN
+ * $Id: intf.c,v 1.1 2003/10/04 18:55:13 gbazin Exp $
+ *
+ * Authors: Stéphane Borel <stef@via.ecp.fr>
+ *          Current modification and breakage for VCD by rocky.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <stdlib.h>                                      /* malloc(), free() */
+#include <string.h>
+#include <unistd.h>
+
+#include <vlc/vlc.h>
+#include <vlc/intf.h>
+
+#include "stream_control.h"
+#include "input_ext-intf.h"
+#include "input_ext-dec.h"
+
+#include "vcd.h"
+
+/*****************************************************************************
+ * intf_sys_t: description and status of interface
+ *****************************************************************************/
+struct intf_sys_t
+{
+    input_thread_t *    p_input;
+    vcd_data_t *        p_vcd;
+
+    vlc_bool_t          b_still;
+    vlc_bool_t          b_inf_still;
+    mtime_t             m_still_time;
+
+#if FINISHED
+    vcdplay_ctrl_t      control;
+#else 
+    int                 control;
+#endif
+    vlc_bool_t          b_click, b_move, b_key_pressed;
+};
+
+/*****************************************************************************
+ * Local prototypes.
+ *****************************************************************************/
+static int  InitThread     ( intf_thread_t *p_intf );
+static int  MouseEvent     ( vlc_object_t *, char const *,
+                             vlc_value_t, vlc_value_t, void * );
+static int  KeyEvent       ( vlc_object_t *, char const *,
+                             vlc_value_t, vlc_value_t, void * );
+
+/* Exported functions */
+static void RunIntf        ( intf_thread_t *p_intf );
+
+/*****************************************************************************
+ * OpenIntf: initialize dummy interface
+ *****************************************************************************/
+int E_(VCDOpenIntf) ( vlc_object_t *p_this )
+{
+    intf_thread_t *p_intf = (intf_thread_t *)p_this;
+
+    printf("+++++Called VCDOpenIntf\n");
+    /* Allocate instance and initialize some members */
+    p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
+    if( p_intf->p_sys == NULL )
+    {
+        return( 1 );
+    };
+
+    p_intf->pf_run = RunIntf;
+
+    p_intf->p_sys->m_still_time = 0;
+    p_intf->p_sys->b_inf_still = 0;
+    p_intf->p_sys->b_still = 0;
+
+    return( 0 );
+}
+
+/*****************************************************************************
+ * CloseIntf: destroy dummy interface
+ *****************************************************************************/
+void E_(VCDCloseIntf) ( vlc_object_t *p_this )
+{
+    intf_thread_t *p_intf = (intf_thread_t *)p_this;
+
+    /* Destroy structure */
+    free( p_intf->p_sys );
+}
+
+
+/*****************************************************************************
+ * RunIntf: main loop
+ *****************************************************************************/
+static void RunIntf( intf_thread_t *p_intf )
+{
+    vlc_object_t *      p_vout = NULL;
+    printf("+++++Called RunIntf\n");
+
+    if( InitThread( p_intf ) < 0 )
+    {
+        msg_Err( p_intf, "can't initialize intf" );
+        return;
+    }
+    msg_Dbg( p_intf, "intf initialized" );
+
+    /* Main loop */
+    while( !p_intf->b_die )
+    {
+      vlc_mutex_lock( &p_intf->change_lock );
+
+        /*
+         * keyboard event
+         */
+        if( p_vout && p_intf->p_sys->b_key_pressed )
+        {
+            p_intf->p_sys->b_key_pressed = VLC_FALSE;
+            
+            printf("++++key pressed...\n");
+        }
+                
+        vlc_mutex_unlock( &p_intf->change_lock );
+
+        if( p_vout == NULL )
+        {
+            p_vout = vlc_object_find( p_intf->p_sys->p_input,
+                                      VLC_OBJECT_VOUT, FIND_CHILD );
+            if( p_vout )
+            {
+                var_AddCallback( p_vout, "mouse-moved", MouseEvent, p_intf );
+                var_AddCallback( p_vout, "mouse-clicked", MouseEvent, p_intf );
+                var_AddCallback( p_vout, "key-pressed", KeyEvent, p_intf );
+            }
+        }
+
+
+        /* Wait a bit */
+        msleep( INTF_IDLE_SLEEP );
+    }
+
+    vlc_object_release( p_intf->p_sys->p_input );
+}
+
+/*****************************************************************************
+ * InitThread:
+ *****************************************************************************/
+static int InitThread( intf_thread_t * p_intf )
+{
+    /* We might need some locking here */
+    if( !p_intf->b_die )
+    {
+        input_thread_t * p_input;
+        vcd_data_t * p_vcd;
+
+        p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_PARENT );
+
+        /* Maybe the input just died */
+        if( p_input == NULL )
+        {
+            return VLC_EGENERIC;
+        }
+
+        p_vcd = (vcd_data_t*)p_input->p_access_data;
+        p_vcd->p_intf = p_intf;
+
+        vlc_mutex_lock( &p_intf->change_lock );
+
+        p_intf->p_sys->p_input = p_input;
+        p_intf->p_sys->p_vcd = p_vcd;
+
+        p_intf->p_sys->b_move = VLC_FALSE;
+        p_intf->p_sys->b_click = VLC_FALSE;
+        p_intf->p_sys->b_key_pressed = VLC_FALSE;
+
+        vlc_mutex_unlock( &p_intf->change_lock );
+
+        return VLC_SUCCESS;
+    }
+    else
+    {
+        return VLC_EGENERIC;
+    }
+}
+
+/*****************************************************************************
+ * MouseEvent: callback for mouse events
+ *****************************************************************************/
+static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
+                       vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    intf_thread_t *p_intf = (intf_thread_t *)p_data;
+
+    vlc_mutex_lock( &p_intf->change_lock );
+
+    if( psz_var[6] == 'c' ) /* "mouse-clicked" */
+    {
+        p_intf->p_sys->b_click = VLC_TRUE;
+    }
+    else if( psz_var[6] == 'm' ) /* "mouse-moved" */
+    {
+        p_intf->p_sys->b_move = VLC_TRUE;
+    }
+
+    vlc_mutex_unlock( &p_intf->change_lock );
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * KeyEvent: callback for keyboard events
+ *****************************************************************************/
+static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
+                       vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    intf_thread_t *p_intf = (intf_thread_t *)p_data;
+    vlc_mutex_lock( &p_intf->change_lock );
+
+    p_intf->p_sys->b_key_pressed = VLC_TRUE;
+    
+    vlc_mutex_unlock( &p_intf->change_lock );
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * dvdIntfStillTime: function provided to demux plugin to request
+ * still images
+ *****************************************************************************/
+int vcdIntfStillTime( intf_thread_t *p_intf, int i_sec )
+{
+    vlc_mutex_lock( &p_intf->change_lock );
+
+    if( i_sec == 0xff )
+    {
+        p_intf->p_sys->b_still = 1;
+        p_intf->p_sys->b_inf_still = 1;
+    }
+    else if( i_sec > 0 )
+    {
+        p_intf->p_sys->b_still = 1;
+        p_intf->p_sys->m_still_time = 1000000 * i_sec;
+    }
+    vlc_mutex_unlock( &p_intf->change_lock );
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * vcdIntfStillTime: function provided to reset still image
+ *****************************************************************************/
+int vcdIntfResetStillTime( intf_thread_t *p_intf )
+{
+    vlc_mutex_lock( &p_intf->change_lock );
+    p_intf->p_sys->m_still_time = 0;
+    input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
+    vlc_mutex_unlock( &p_intf->change_lock );
+
+    return VLC_SUCCESS;
+}
diff --git a/modules/access/vcdx/intf.h b/modules/access/vcdx/intf.h
new file mode 100644 (file)
index 0000000..fb72e11
--- /dev/null
@@ -0,0 +1,26 @@
+/*****************************************************************************
+ * intf.h: send info to intf.
+ *****************************************************************************
+ * Copyright (C) 2001 VideoLAN
+ * $Id: intf.h,v 1.1 2003/10/04 18:55:13 gbazin Exp $
+ *
+ * Author: Stéphane Borel <stef@via.ecp.fr>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+int vcdIntfStillTime( struct intf_thread_t *, int );
+int vcdIntfResetStillTime( intf_thread_t *p_intf );
+
diff --git a/modules/access/vcdx/vcd.c b/modules/access/vcdx/vcd.c
new file mode 100644 (file)
index 0000000..8567ee8
--- /dev/null
@@ -0,0 +1,1132 @@
+/*****************************************************************************
+ * vcd.c : VCD input module for vlc
+ *         using libcdio, libvcd and libvcdinfo. vlc-specific things tend
+ *         to go here.
+ *****************************************************************************
+ * Copyright (C) 2000 VideoLAN
+ * $Id: vcd.c,v 1.1 2003/10/04 18:55:13 gbazin Exp $
+ *
+ * Authors: Johan Bilien <jobi@via.ecp.fr>
+ *          Rocky Bernstein <rocky@panix.com> 
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#include <vlc/vlc.h>
+#include <vlc/input.h>
+
+#include "../../demux/mpeg/system.h"
+#include "vcd.h"
+#include "intf.h"
+#include "vcdplayer.h"
+
+#include <cdio/cdio.h>
+#include <cdio/cd_types.h>
+#include <cdio/logging.h>
+#include <cdio/util.h>
+#include <libvcd/info.h>
+#include <libvcd/logging.h>
+
+#include "cdrom.h"
+
+/* how many blocks VCDRead will read in each loop */
+#define VCD_BLOCKS_ONCE 20
+#define VCD_DATA_ONCE   (VCD_BLOCKS_ONCE * M2F2_SECTOR_SIZE)
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+
+/* First those which are accessed from outside (via pointers). */
+static int  VCDOpen         ( vlc_object_t * );
+static void VCDClose        ( vlc_object_t * );
+static int  VCDRead         ( input_thread_t *, byte_t *, size_t );
+static int  VCDRead         ( input_thread_t *, byte_t *, size_t );
+static int  VCDSetProgram   ( input_thread_t *, pgrm_descriptor_t * );
+
+/* Now those which are strictly internal */
+static void VCDSetOrigin    ( input_thread_t *, lsn_t origin_lsn, 
+                              lsn_t cur_lsn, lsn_t end_lsn, 
+                              int cur_entry, track_t track );
+static int  VCDEntryPoints  ( input_thread_t * );
+static int  VCDLIDs         ( input_thread_t * );
+static int  VCDSegments     ( input_thread_t * );
+static void VCDTracks       ( input_thread_t * );
+static int  VCDReadSector   ( vlc_object_t *p_this, 
+                              const vcdinfo_obj_t *p_vcd, lsn_t cur_lsn, 
+                              byte_t * p_buffer );
+static char *VCDParse       ( input_thread_t *, 
+                              /*out*/ vcdinfo_itemid_t * p_itemid );
+
+static void VCDUpdateVar( input_thread_t *p_input, int i_entry, int i_action,
+                          const char *varname, const char *label );
+
+static vcdinfo_obj_t *vcd_Open   ( vlc_object_t *p_this, const char *psz_dev );
+
+static int debug_callback   ( vlc_object_t *p_this, const char *psz_name,
+                               vlc_value_t oldval, vlc_value_t val, 
+                               void *p_data );
+
+#define DEBUG_TEXT N_("set debug mask for additional debugging.")
+#define DEBUG_LONGTEXT N_( \
+    "This integer when viewed in binary is a debugging mask\n" \
+    "MRL             1\n" \
+    "external call   2\n" \
+    "all calls       4\n" \
+    "LSN             8\n" \
+    "PBC      (10)  16\n" \
+    "libcdio  (20)  32\n" \
+    "seeks    (40)  64\n" \
+    "still    (80) 128\n" \
+    "vcdinfo (100) 256\n" )
+
+/*****************************************************************************
+ * Exported prototypes
+ *****************************************************************************/
+int  E_(VCDOpen)      ( vlc_object_t * );
+void E_(VCDClose)     ( vlc_object_t * );
+int  E_(VCDOpenIntf)  ( vlc_object_t * );
+void E_(VCDCloseIntf) ( vlc_object_t * );
+int  E_(InitVCD)      ( vlc_object_t * );
+void E_(EndVCD)       ( vlc_object_t * );
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+vlc_module_begin();
+    add_usage_hint( N_("vcdx:[device-or-file][@{P,S,T}num]") );
+    set_description( _("Video CD (VCD 1.0, 1.1, 2.0, SVCD, HQVCD) input") );
+    set_capability( "access", 85 /* slightly higher than vcd */ );
+    set_callbacks( E_(VCDOpen), E_(VCDClose) );
+    add_shortcut( "vcd" );
+    add_shortcut( "vcdx" );
+
+    /* Configuration options */
+    add_category_hint( N_("VCDX"), NULL, VLC_TRUE );
+    add_integer ( MODULE_STRING "-debug", 0, debug_callback, DEBUG_TEXT, 
+                  DEBUG_LONGTEXT, VLC_TRUE );
+
+#ifdef FIXED
+    add_submodule();
+        set_capability( "demux", 0 );
+        set_callbacks( E_(InitVCD), E_(EndVCD) );
+#endif
+
+    add_submodule();
+        set_capability( "interface", 0 );
+        set_callbacks( E_(VCDOpenIntf), E_(VCDCloseIntf) );
+vlc_module_end();
+
+/****************************************************************************
+ * Private functions
+ ****************************************************************************/
+/* FIXME: This variable is a hack. Would be nice to eliminate. */
+static input_thread_t *p_vcd_input = NULL;
+
+static int
+debug_callback   ( vlc_object_t *p_this, const char *psz_name,
+                   vlc_value_t oldval, vlc_value_t val, void *p_data )
+{
+  thread_vcd_data_t *p_vcd;
+
+  if (NULL == p_vcd_input) return VLC_EGENERIC;
+  
+  p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
+
+  if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
+    msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d", 
+             p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
+  }
+  p_vcd->i_debug = val.i_int;
+  return VLC_SUCCESS;
+}
+
+/* process messages that originate from libcdio. */
+static void
+cdio_log_handler (cdio_log_level_t level, const char message[])
+{
+  thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
+  switch (level) {
+  case CDIO_LOG_DEBUG:
+  case CDIO_LOG_INFO:
+    if (p_vcd->i_debug & INPUT_DBG_CDIO) 
+      msg_Dbg( p_vcd_input, message);
+    break;
+  case CDIO_LOG_WARN:
+    msg_Warn( p_vcd_input, message);
+    break;
+  case CDIO_LOG_ERROR:
+  case CDIO_LOG_ASSERT:
+    msg_Err( p_vcd_input, message);
+    break;
+  default:
+    msg_Warn( p_vcd_input, message,
+            _("The above message had unknown vcdimager log level"), 
+            level);
+  }
+  return;
+}
+
+/* process messages that originate from vcdinfo. */
+static void
+vcd_log_handler (vcd_log_level_t level, const char message[])
+{
+  thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
+  switch (level) {
+  case VCD_LOG_DEBUG:
+  case VCD_LOG_INFO:
+    if (p_vcd->i_debug & INPUT_DBG_VCDINFO)
+      msg_Dbg( p_vcd_input, message);
+    break;
+  case VCD_LOG_WARN:
+    msg_Warn( p_vcd_input, message);
+    break;
+  case VCD_LOG_ERROR:
+  case VCD_LOG_ASSERT:
+    msg_Err( p_vcd_input, message);
+    break;
+  default:
+    msg_Warn( p_vcd_input, "%s\n%s %d", message,
+            _("The above message had unknown vcdimager log level"), 
+            level);
+  }
+  return;
+}
+
+/*
+ * Data reading functions
+ */
+
+/*****************************************************************************
+  VCDOpen: open VCD.
+  read in meta-information about VCD: the number of tracks, segments, 
+  entries, size and starting information. Then set up state variables so
+  that we read/seek starting at the location specified.
+
+  On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM, 
+  and VLC_EGENERIC for some other error.
+ *****************************************************************************/
+static int 
+VCDOpen( vlc_object_t *p_this )
+{
+    input_thread_t *        p_input = (input_thread_t *)p_this;
+    thread_vcd_data_t *     p_vcd;
+    char *                  psz_source;
+    vcdinfo_itemid_t        itemid;
+    bool                    play_ok;
+    
+    p_input->pf_read        = VCDRead;
+    p_input->pf_seek        = VCDSeek;
+    p_input->pf_set_area    = VCDSetArea;
+    p_input->pf_set_program = VCDSetProgram;
+
+    p_vcd = malloc( sizeof(thread_vcd_data_t) );
+
+    if( p_vcd == NULL )
+    {
+        LOG_ERR ("out of memory" );
+        return VLC_ENOMEM;
+    }
+
+    p_input->p_access_data = (void *)p_vcd;
+    p_vcd->i_debug         = config_GetInt( p_this, MODULE_STRING "-debug" );
+    psz_source             = VCDParse( p_input, &itemid );
+
+    if ( NULL == psz_source ) 
+    {
+      free( p_vcd );
+      return( VLC_EGENERIC );
+    }
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "%s", psz_source );
+
+    p_vcd->p_segments = NULL;
+    p_vcd->p_entries  = NULL;
+    
+    /* set up input  */
+    p_input->i_mtu = VCD_DATA_ONCE;
+
+    vlc_mutex_lock( &p_input->stream.stream_lock );
+
+    /* If we are here we can control the pace... */
+    p_input->stream.b_pace_control = 1;
+
+    p_input->stream.b_seekable = 1;
+    p_input->stream.p_selected_area->i_size = 0;
+    p_input->stream.p_selected_area->i_tell = 0;
+
+    vlc_mutex_unlock( &p_input->stream.stream_lock );
+
+    if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
+    {
+        msg_Warn( p_input, "could not open %s", psz_source );
+        free( psz_source );
+        free( p_vcd );
+        return VLC_EGENERIC;
+    }
+
+    /* Get track information. */
+    p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
+                                            vcdinfo_get_cd_image(p_vcd->vcd), 
+                                            &p_vcd->p_sectors );
+    free( psz_source );
+    if( p_vcd->num_tracks < 0 )
+        LOG_ERR ("unable to count tracks" );
+    else if( p_vcd->num_tracks <= 1 )
+        LOG_ERR ("no movie tracks found" );
+    if( p_vcd->num_tracks <= 1)
+    {
+        vcdinfo_close( p_vcd->vcd );
+        free( p_vcd );
+        return VLC_EGENERIC;
+    }
+
+    /* Set stream and area data */
+    vlc_mutex_lock( &p_input->stream.stream_lock );
+
+    /* Initialize ES structures */
+    input_InitStream( p_input, sizeof( stream_ps_data_t ) );
+
+    /* disc input method */
+    p_input->stream.i_method = INPUT_METHOD_VCD;
+
+    p_input->stream.i_area_nb = 1;
+    
+
+    /* Initialize segment information. */
+    VCDSegments( p_input );
+    
+    /* Initialize track area information. */
+    VCDTracks( p_input );
+    
+    if( VCDEntryPoints( p_input ) < 0 )
+    {
+        msg_Warn( p_input, "could not read entry points, will not use them" );
+        p_vcd->b_valid_ep = false;
+    }
+
+    if( VCDLIDs( p_input ) < 0 )
+    {
+        msg_Warn( p_input, "could not read entry LIDs" );
+    }
+
+    play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
+    
+    vlc_mutex_unlock( &p_input->stream.stream_lock );
+
+    if ( ! play_ok ) {
+      vcdinfo_close( p_vcd->vcd );
+      free( p_vcd );
+      return VLC_EGENERIC;
+    }
+
+    if( !p_input->psz_demux || !*p_input->psz_demux )
+    {
+#if FIXED
+      p_input->psz_demux = "vcdx";
+#else
+      p_input->psz_demux = "ps";
+#endif
+    }
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * VCDClose: closes VCD releasing allocated memory.
+ *****************************************************************************/
+static void 
+VCDClose( vlc_object_t *p_this )
+{
+    input_thread_t *   p_input = (input_thread_t *)p_this;
+    thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
+    vcdinfo_close( p_vcd->vcd );
+
+    free( p_vcd->p_entries );
+    free( p_vcd->p_segments );
+    free( p_vcd );
+    p_vcd_input = NULL;
+}
+
+/*****************************************************************************
+ * VCDRead: reads i_len bytes from the VCD into p_buffer.
+ *****************************************************************************
+ * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
+ * bytes.
+ *****************************************************************************/
+static int 
+VCDRead( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
+{
+    thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
+    int                     i_blocks;
+    int                     i_index;
+    int                     i_read;
+    byte_t                  p_last_sector[ M2F2_SECTOR_SIZE ];
+
+    i_read = 0;
+
+    /* Compute the number of blocks we have to read */
+
+    i_blocks = i_len / M2F2_SECTOR_SIZE;
+
+    for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
+    {
+
+      if ( p_vcd->cur_lsn == p_vcd->end_lsn ) {
+        vcdplayer_read_status_t read_status;
+
+        /* We've run off of the end of this entry. Do we continue or stop? */
+        dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
+                   "end reached, cur: %u", p_vcd->cur_lsn );
+
+        read_status = vcdplayer_pbc_is_on( p_vcd ) 
+          ? vcdplayer_pbc_nav( p_input ) 
+          : vcdplayer_non_pbc_nav( p_input );
+
+        switch (read_status) {
+        case READ_END:
+          /* End reached. Return NULL to indicated this. */
+        case READ_ERROR:
+          /* Some sort of error. */
+          return i_read;
+        case READ_STILL_FRAME: 
+          {
+            byte_t * p_buf = p_buffer;
+            p_buf += (i_index*M2F2_SECTOR_SIZE);
+            memset(p_buf, 0, M2F2_SECTOR_SIZE);
+            p_buf += 2;
+            *p_buf = 0x01;
+            dbg_print(INPUT_DBG_STILL, "Handled still event\n");
+            return i_read + M2F2_SECTOR_SIZE;
+          }
+        default:
+        case READ_BLOCK:
+          break;
+        }
+      }
+
+      if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
+                          p_vcd->cur_lsn, 
+                          p_buffer + (i_index*M2F2_SECTOR_SIZE) ) < 0 )
+        {
+          LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
+          return -1;
+        }
+      
+      p_vcd->cur_lsn ++;
+      
+      /* Update chapter */
+      if( p_vcd->b_valid_ep &&
+          /* FIXME kludge so that read does not update chapter
+           * when a manual chapter change was requested and not
+           * yet accomplished */
+          !p_input->stream.p_new_area )
+        {
+          unsigned int i_entry = p_input->stream.p_selected_area->i_part;
+          
+          vlc_mutex_lock( &p_input->stream.stream_lock );
+          
+          if( i_entry < p_vcd->num_entries &&
+              p_vcd->cur_lsn >= p_vcd->p_entries[i_entry+1] )
+            {
+              dbg_print( INPUT_DBG_PBC, 
+                         "new entry, i_entry %d, sector %d, es %d",
+                         i_entry, p_vcd->cur_lsn, 
+                         p_vcd->p_entries[i_entry] );
+              p_vcd->play_item.num = 
+                ++ p_input->stream.p_selected_area->i_part;
+              p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
+              VCDUpdateVar( p_input, p_vcd->play_item.num, VLC_VAR_SETVALUE,
+                            "chapter", "Setting entry" );
+            }
+          vlc_mutex_unlock( &p_input->stream.stream_lock );
+        }
+
+        i_read += M2F2_SECTOR_SIZE;
+    }
+
+    if ( i_len % M2F2_SECTOR_SIZE ) /* this should not happen */
+    {
+        if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
+                            p_vcd->cur_lsn, p_last_sector ) < 0 )
+        {
+            LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
+            return -1;
+        }
+
+        p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * M2F2_SECTOR_SIZE,
+                                   p_last_sector, i_len % M2F2_SECTOR_SIZE );
+        i_read += i_len % M2F2_SECTOR_SIZE;
+    }
+
+    return i_read;
+}
+
+
+/*****************************************************************************
+ * VCDSetProgram: Does nothing since a VCD is mono_program
+ *****************************************************************************/
+static int 
+VCDSetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_program)
+{
+    thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDSetProgram" );
+    return 0;
+}
+
+
+/*****************************************************************************
+ * VCDSetArea: initialize internal data structures and input stream data 
+   so set subsequent reading and seeking to reflect that we are
+   at track x, entry or segment y.
+   This is called for each user navigation request, e.g. the GUI 
+   Chapter/Title selections or in initial MRL parsing. 
+ ****************************************************************************/
+int 
+VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
+{
+    thread_vcd_data_t *p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
+    unsigned int i_entry = p_area->i_part;
+    track_t i_track      = p_area->i_id;
+    int old_seekable     = p_input->stream.b_seekable;
+    unsigned int i_nb    = p_area->i_plugin_data + p_area->i_part_nb;
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT),
+               "track: %d, entry %d, seekable %d",
+               i_track, i_entry, old_seekable );
+
+    /* we can't use the interface slider until initilization is complete */
+    p_input->stream.b_seekable = 0;
+
+    if( p_area != p_input->stream.p_selected_area )
+    {
+        unsigned int i;
+
+        /* If is the result of a track change, make the entry valid. */
+        if (i_entry < p_area->i_plugin_data || i_entry >= i_nb) 
+          i_entry = p_area->i_plugin_data;
+
+        /* Change the default area */
+        p_input->stream.p_selected_area = p_area;
+
+        /* Update the navigation variables without triggering a callback */
+        VCDUpdateVar( p_input, i_track, VLC_VAR_SETVALUE, "title", 
+                      "Setting track");
+
+        var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
+        for( i = p_area->i_plugin_data; i < i_nb; i++ )
+        {
+          VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE, 
+                        "chapter",  "Adding entry choice");
+        }
+    }
+
+    if (i_track == 0) 
+      VCDSetOrigin( p_input, p_vcd->p_segments[i_entry], 
+                    p_vcd->p_segments[i_entry], p_vcd->p_segments[i_entry+1],
+                    i_entry, 0 );
+    else
+      VCDSetOrigin( p_input, p_vcd->p_sectors[i_track], 
+                    vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry), 
+                    p_vcd->p_sectors[i_track+1],
+                    i_entry, i_track );
+
+    p_input->stream.b_seekable = old_seekable;
+    /* warn interface that something has changed */
+    p_input->stream.b_changed = 1;
+
+    return VLC_SUCCESS;
+}
+
+
+/****************************************************************************
+ * VCDSeek
+ ****************************************************************************/
+void 
+VCDSeek( input_thread_t * p_input, off_t i_off )
+{
+    thread_vcd_data_t * p_vcd;
+    unsigned int i_entry=0; /* invalid entry */
+
+    p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
+
+    p_vcd->cur_lsn = p_vcd->origin_lsn + (i_off / (off_t)M2F2_SECTOR_SIZE);
+
+    vlc_mutex_lock( &p_input->stream.stream_lock );
+#define p_area p_input->stream.p_selected_area
+    /* Find entry */
+    if( p_vcd->b_valid_ep )
+    {
+        for( i_entry = 1 ; i_entry < p_vcd->num_entries ; i_entry ++ )
+        {
+            if( p_vcd->cur_lsn < p_vcd->p_entries[i_entry] )
+            {
+              VCDUpdateVar( p_input, i_entry, VLC_VAR_SETVALUE, 
+                            "chapter", "Setting entry" );
+              break;
+            }
+        }
+        p_vcd->play_item.num  = p_area->i_part = i_entry;
+        p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
+    }
+#undef p_area
+
+    p_input->stream.p_selected_area->i_tell = i_off;
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
+    "orig %d, cur: %d, offset: %lld, start: %lld, entry %d", 
+               p_vcd->origin_lsn, p_vcd->cur_lsn, i_off, 
+               p_input->stream.p_selected_area->i_start, i_entry );
+
+    vlc_mutex_unlock( &p_input->stream.stream_lock );
+}
+
+/*****************************************************************************
+  VCDPlay: set up internal structures so seeking/reading places an item.
+  itemid: the thing to play.
+  user_entry: true if itemid is a user selection (rather than internally-
+  generated selection such as via PBC) in which case we may have to adjust 
+  for differences in numbering. 
+ *****************************************************************************/
+int
+VCDPlay( input_thread_t *p_input, vcdinfo_itemid_t itemid )
+{
+    thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
+    input_area_t *          p_area;
+    
+    p_vcd->in_still = 0;
+
+#define area p_input->stream.pp_areas
+
+    switch (itemid.type) {
+    case VCDINFO_ITEM_TYPE_TRACK:
+
+      /* Valid tracks go from 1...num_tracks-1, because track 0 is unplayable.
+       */
+
+      if (itemid.num == 0 || itemid.num >= p_vcd->num_tracks) {
+        LOG_ERR ("Invalid track number %d", itemid.num );
+        return VLC_EGENERIC;
+      }
+      p_area           = area[itemid.num];
+      p_area->i_part   = p_area->i_plugin_data;
+      p_input->stream.b_seekable = 1;
+      break;
+    case VCDINFO_ITEM_TYPE_SEGMENT: 
+      /* Valid segments go from 0...num_segments-1. */
+      if (itemid.num >= p_vcd->num_segments) {
+        LOG_ERR ( "Invalid segment number: %d", itemid.num );
+        return VLC_EGENERIC;
+      } else {
+        vcdinfo_video_segment_type_t segtype = 
+          vcdinfo_get_video_type(p_vcd->vcd, itemid.num);
+        
+        dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d", 
+                  vcdinfo_video_type2str(p_vcd->vcd, itemid.num), 
+                  (int) segtype, itemid.num);
+        
+        p_area           = area[0];
+        p_area->i_part   = itemid.num;
+        
+        switch (segtype)
+          {
+          case VCDINFO_FILES_VIDEO_NTSC_STILL:
+          case VCDINFO_FILES_VIDEO_NTSC_STILL2:
+          case VCDINFO_FILES_VIDEO_PAL_STILL:
+          case VCDINFO_FILES_VIDEO_PAL_STILL2:
+            p_input->stream.b_seekable = 0;
+            p_vcd->in_still = -5;
+            break;
+          default:
+            p_input->stream.b_seekable = 1;
+            p_vcd->in_still = 0;
+          }
+      }
+      break;
+      
+    case VCDINFO_ITEM_TYPE_LID:
+      /* LIDs go from 1..num_lids. */
+      if (itemid.num == 0 || itemid.num > p_vcd->num_lids) {
+        LOG_ERR ( "Invalid LID number: %d", itemid.num );
+        return VLC_EGENERIC;
+      } else {
+        p_vcd->cur_lid = itemid.num;
+        vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
+    
+        switch (p_vcd->pxd.descriptor_type) {
+      
+        case PSD_TYPE_SELECTION_LIST:
+        case PSD_TYPE_EXT_SELECTION_LIST: {
+          vcdinfo_itemid_t trans_itemid;
+          uint16_t trans_itemid_num;
+          
+          if (p_vcd->pxd.psd == NULL) return VLC_EGENERIC;
+          trans_itemid_num  = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
+          vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
+          p_vcd->loop_count = 1;
+          p_vcd->loop_item  = trans_itemid;
+          return VCDPlay( p_input, trans_itemid );
+          break;
+        }
+          
+        case PSD_TYPE_PLAY_LIST: {
+          if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
+          p_vcd->pdi = -1;
+          return vcdplayer_inc_play_item(p_input) 
+            ? VLC_SUCCESS : VLC_EGENERIC;
+          break;
+        }
+          
+        case PSD_TYPE_END_LIST:
+        case PSD_TYPE_COMMAND_LIST:
+          
+        default:
+          ;
+        }
+      }
+      return VLC_EGENERIC;
+    case VCDINFO_ITEM_TYPE_ENTRY:
+      /* Entries go from 0..num_entries-1. */
+      if (itemid.num >= p_vcd->num_entries) {
+        LOG_ERR ("Invalid entry number: %d", itemid.num );
+        return VLC_EGENERIC;
+      } else {
+        track_t cur_track  = vcdinfo_get_track(p_vcd->vcd,  itemid.num);
+        p_area             = area[cur_track];
+        p_area->i_part     = itemid.num;
+        p_input->stream.b_seekable = 1;
+      }
+      break;
+    default:
+      LOG_ERR ("unknown entry type" );
+      return VLC_EGENERIC;
+    }
+
+    VCDSetArea( p_input, p_area );
+
+#undef area
+
+    p_vcd->play_item = itemid;
+
+    dbg_print( (INPUT_DBG_CALL), 
+               "i_start %lld, i_size: %lld, i_tell: %lld, lsn %d", 
+               p_area->i_start, p_area->i_size, 
+               p_area->i_tell, p_vcd->cur_lsn );
+        
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+  VCDEntryPoints: Reads the information about the entry points on the disc
+  and initializes area information with that.
+  Before calling this track information should have been read in.
+ *****************************************************************************/
+static int 
+VCDEntryPoints( input_thread_t * p_input )
+{
+    thread_vcd_data_t *               p_vcd;
+    unsigned int                      i_nb;
+    unsigned int                      i, i_entry_index = 0;
+    unsigned int                      i_previous_track = CDIO_INVALID_TRACK;
+
+    p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
+
+    i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
+    if (0 == i_nb) 
+      return -1;
+    
+    p_vcd->p_entries  = malloc( sizeof( lba_t ) * i_nb );
+
+    if( p_vcd->p_entries == NULL )
+    {
+        LOG_ERR ("not enough memory for entry points treatment" );
+        return -1;
+    }
+
+    p_vcd->num_entries = 0;
+
+    for( i = 0 ; i < i_nb ; i++ )
+    {
+        track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
+        if( i_track <= p_input->stream.i_area_nb )
+        {
+            p_vcd->p_entries[i] = 
+              vcdinfo_get_entry_lsn(p_vcd->vcd, i);
+            p_input->stream.pp_areas[i_track]->i_part_nb ++;
+
+            /* if this entry belongs to a new track */
+            if( i_track != i_previous_track )
+            {
+                /* i_plugin_data is used to store the first entry of the area*/
+                p_input->stream.pp_areas[i_track]->i_plugin_data =
+                                                            i_entry_index;
+                i_previous_track = i_track;
+                p_input->stream.pp_areas[i_track]->i_part_nb = 1;
+            }
+            i_entry_index ++;
+            p_vcd->num_entries ++;
+        }
+        else
+            msg_Warn( p_input, "wrong track number found in entry points" );
+    }
+    p_vcd->b_valid_ep = true;
+    return 0;
+}
+
+/*****************************************************************************
+ * VCDSegments: Reads the information about the segments the disc.
+ *****************************************************************************/
+static int
+VCDSegments( input_thread_t * p_input )
+{
+    thread_vcd_data_t * p_vcd;
+    unsigned int        i;
+    unsigned int        num_segments;
+    
+
+    p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
+    num_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
+
+#define area p_input->stream.pp_areas
+
+    /* area 0 is reserved for segments. Set Absolute start offset
+         and size */
+    area[0]->i_plugin_data = 0;
+    input_DelArea( p_input, area[0] );
+    input_AddArea( p_input, 0, 0 );
+    
+    area[0]->i_start = (off_t)p_vcd->p_sectors[0] 
+      * (off_t)M2F2_SECTOR_SIZE;
+    area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
+      * (off_t)M2F2_SECTOR_SIZE;
+    
+    /* Default Segment  */
+    area[0]->i_part = 0;
+    
+    /* i_plugin_data is used to store which entry point is the first
+       of the track (area) */
+    area[0]->i_plugin_data = 0;
+
+    area[0]->i_part_nb = 0;
+    
+    dbg_print( INPUT_DBG_MRL, "area id %d, for segment track %d", 
+               area[0]->i_id, 0 );
+
+    if (num_segments == 0) return 0;
+
+    /* We have one additional segment allocated so we can get the size
+       by subtracting seg[i+1] - seg[i].
+     */
+    p_vcd->p_segments = malloc( sizeof( lba_t ) * (num_segments+1) );
+    if( p_vcd->p_segments == NULL )
+    {
+        LOG_ERR ("not enough memory for segment treatment" );
+        return -1;
+    }
+
+    /* Update the navigation variables without triggering a callback */
+    VCDUpdateVar( p_input, 0, VLC_VAR_SETVALUE, "title", "Setting track" );
+    var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
+    
+    for( i = 0 ; i < num_segments ; i++ )
+    {
+      p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
+      area[0]->i_part_nb ++;
+      VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE, 
+                    "chapter", "Adding segment choice");
+    }
+
+#undef area
+
+    p_vcd->p_segments[num_segments] = p_vcd->p_segments[num_segments-1]+
+      vcdinfo_get_seg_sector_count(p_vcd->vcd, num_segments-1);
+    
+    return 0;
+}
+
+/*****************************************************************************
+ VCDTracks: initializes area information. 
+ Before calling this track information should have been read in.
+ *****************************************************************************/
+static void
+VCDTracks( input_thread_t * p_input )
+{
+    thread_vcd_data_t * p_vcd;
+    unsigned int        i;
+
+    p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
+
+#define area p_input->stream.pp_areas
+
+    /* We start area addressing for tracks at 1 since the default area 0
+       is reserved for segments */
+
+    for( i = 1 ; i < p_vcd->num_tracks ; i++ )
+    {
+        /* Tracks are Program Chains */
+        input_AddArea( p_input, i, i );
+
+        /* Absolute start byte offset and byte size */
+        area[i]->i_start = (off_t) p_vcd->p_sectors[i] 
+                           * (off_t)M2F2_SECTOR_SIZE;
+        area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
+                           * (off_t)M2F2_SECTOR_SIZE;
+
+        /* Current entry being played in track */
+        area[i]->i_part = 0;
+
+        /* i_plugin_data is used to store which entry point is the first
+         * of the track (area) */
+        area[i]->i_plugin_data = 0;
+
+        dbg_print( INPUT_DBG_MRL, 
+                   "area[%d] id: %d, i_start: %lld, i_size: %lld", 
+                   i, area[i]->i_id, area[i]->i_start, area[i]->i_size );
+    }
+
+#undef area
+
+    return ;
+}
+
+/*****************************************************************************
+  VCDLIDs: Reads the LIST IDs from the LOT.
+ *****************************************************************************/
+static int 
+VCDLIDs( input_thread_t * p_input )
+{
+    thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
+
+    p_vcd->num_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
+    p_vcd->cur_lid  = VCDINFO_INVALID_ENTRY;
+
+    if (vcdinfo_read_psd (p_vcd->vcd)) {
+      
+      vcdinfo_visit_lot (p_vcd->vcd, false);
+      
+      if (vcdinfo_get_psd_x_size(p_vcd->vcd))
+        vcdinfo_visit_lot (p_vcd->vcd, true);
+    }
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL), 
+               "num LIDs=%d", p_vcd->num_lids);
+
+    return 0;
+}
+
+/*****************************************************************************
+ * VCDParse: parse command line
+ *****************************************************************************/
+static char * 
+VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid )
+{
+    thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
+    char *             psz_parser;
+    char *             psz_source;
+    char *             psz_next;
+
+    p_itemid->type=VCDINFO_ITEM_TYPE_TRACK;
+    p_itemid->num=1;
+    
+#ifdef WIN32
+    /* On Win32 we want the VCD access plugin to be explicitly requested,
+     * we end up with lots of problems otherwise */
+    if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
+#endif
+
+    if( !p_input->psz_name )
+    {
+        return NULL;
+    }
+
+    psz_parser = psz_source = strdup( p_input->psz_name );
+
+    /* Parse input string :
+     * [device][@[type][title]] */
+    while( *psz_parser && *psz_parser != '@' )
+    {
+        psz_parser++;
+    }
+
+    if( *psz_parser == '@' )
+    {
+      /* Found the divide between the source name and the 
+         type+entry number. */
+      unsigned int num;
+      
+      *psz_parser = '\0';
+      ++psz_parser;
+      if( *psz_parser )
+        {
+          switch(*psz_parser) {
+          case 'E': 
+            p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
+            ++psz_parser;
+            break;
+          case 'P': 
+            p_itemid->type = VCDINFO_ITEM_TYPE_LID;
+            ++psz_parser;
+            break;
+          case 'S': 
+            p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
+            ++psz_parser;
+            break;
+          case 'T': 
+            p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
+            ++psz_parser;
+            break;
+          default: ;
+          }
+        }
+      
+      num = strtol( psz_parser, &psz_next, 10 );
+      if ( *psz_parser != '\0' && *psz_next == '\0') 
+        {
+          p_itemid->num = num;
+        }
+      
+    }
+
+    if( !*psz_source ) {
+
+      /* No source specified, so figure it out. */
+      if( !p_input->psz_access ) return NULL;
+      
+      psz_source = config_GetPsz( p_input, MODULE_STRING "-device" );
+
+      if( !psz_source ) {
+        /* Scan for a CD with a VCD in it. */
+        char **cd_drives = cdio_get_devices_with_cap(NULL, 
+                            (CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD
+                             |CDIO_FS_ANAL_VIDEOCD|CDIO_FS_UNKNOWN),
+                                                     true);
+        if (NULL == cd_drives) return NULL;
+        if (cd_drives[0] == NULL) {
+          cdio_free_device_list(cd_drives);
+          return NULL;
+        }
+        psz_source = strdup(cd_drives[0]);
+        cdio_free_device_list(cd_drives);
+      }
+    }
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL), 
+               "source=%s entry=%d type=%d",
+               psz_source, p_itemid->num, p_itemid->type);
+
+    return psz_source;
+}
+
+/* 
+   Set's start origin subsequent seeks/reads
+*/
+static void 
+VCDSetOrigin( input_thread_t *p_input, lsn_t origin_lsn, 
+              lsn_t cur_lsn, lsn_t end_lsn, int cur_entry, track_t cur_track )
+{
+  thread_vcd_data_t * p_vcd  = (thread_vcd_data_t *) p_input->p_access_data;
+
+  p_vcd->origin_lsn = origin_lsn;
+  p_vcd->cur_lsn    = cur_lsn;
+  p_vcd->end_lsn    = end_lsn;
+  p_vcd->cur_track  = cur_track;
+  p_vcd->play_item.num  = cur_entry;
+  p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
+  
+  dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
+             "origin: %d, cur_lsn: %d, end_lsn: %d, entry: %d, track: %d",
+             origin_lsn, cur_lsn, end_lsn, cur_entry, cur_track );
+
+  p_input->stream.p_selected_area->i_tell =
+    (off_t) (p_vcd->cur_lsn - p_vcd->origin_lsn) * (off_t)M2F2_SECTOR_SIZE;
+
+  VCDUpdateVar( p_input, cur_entry, VLC_VAR_SETVALUE, 
+                "chapter", "Setting entry");
+}
+
+/*****************************************************************************
+ * vcd_Open: Opens a VCD device or file and returns an opaque handle
+ *****************************************************************************/
+static vcdinfo_obj_t *
+vcd_Open( vlc_object_t *p_this, const char *psz_dev )
+{
+    vcdinfo_obj_t *p_vcdobj;
+    char  *actual_dev;
+
+    if( !psz_dev ) return NULL;
+
+    /* Set where to log errors messages from libcdio. */
+    p_vcd_input = (input_thread_t *)p_this;
+    cdio_log_set_handler ( cdio_log_handler );
+    vcd_log_set_handler ( vcd_log_handler );
+    
+    actual_dev=strdup(psz_dev);
+    if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) != 
+         VCDINFO_OPEN_VCD) {
+      free(actual_dev);
+      return NULL;
+    }
+    free(actual_dev);
+
+    return p_vcdobj;
+}
+
+/****************************************************************************
+ * VCDReadSector: Read a sector (2324 bytes)
+ ****************************************************************************/
+static int 
+VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
+               lsn_t cur_lsn, byte_t * p_buffer )
+{
+  typedef struct {
+    uint8_t subheader   [8];
+    uint8_t data        [M2F2_SECTOR_SIZE];
+  } vcdsector_t;
+  vcdsector_t vcd_sector;
+  
+  if (cdio_read_mode2_sector(vcdinfo_get_cd_image(p_vcd), 
+                             &vcd_sector, cur_lsn, true) 
+      != 0)
+    {
+      msg_Warn( p_this, "Could not read LSN %d", cur_lsn );
+      return -1;
+    }
+    
+  memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
+  
+  return( 0 );
+}
+
+/****************************************************************************
+ Update the "varname" variable to i_num without triggering a callback.
+****************************************************************************/
+static void
+VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
+              const char *varname, const char *label)
+{
+  vlc_value_t val;
+  val.i_int = i_num;
+  if (NULL != p_vcd_input) {
+    thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
+    dbg_print( INPUT_DBG_PBC, "%s %d", label, i_num );
+  }
+  var_Change( p_input, varname, i_action, &val, NULL );
+}
diff --git a/modules/access/vcdx/vcd.h b/modules/access/vcdx/vcd.h
new file mode 100644 (file)
index 0000000..fc6fad6
--- /dev/null
@@ -0,0 +1,60 @@
+/*****************************************************************************
+ * vcd.h : VCD input module for vlc
+ *          using libcdio, libvcd and libvcdinfo
+ *****************************************************************************
+ * Copyright (C) 2003 VideoLAN
+ * $Id: vcd.h,v 1.1 2003/10/04 18:55:13 gbazin Exp $
+ *
+ * Authors: Rocky Bernstein <rocky@panix.com> 
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+#include <libvcd/info.h>
+
+/*****************************************************************************
+ * dvd_data_t: structure for communication between dvdplay access, demux
+ * and intf.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * vcd_data_t: structure for communication between access and intf.
+ *****************************************************************************/
+typedef struct
+{
+#if FINISHED
+    vcdplay_ptr             vmg;
+#endif
+    intf_thread_t *         p_intf;
+
+#if DEMUX_FINISHED
+    int                     i_audio_nb;
+    int                     i_spu_nb;
+#endif
+
+    int                     i_still_time;
+    vlc_bool_t              b_end_of_cell;
+
+#if FINISHED
+    vcdplay_event_t         event;
+    vcdplay_ctrl_t          control;   
+    vcdplay_highlight_t     hli;
+#endif
+
+} vcd_data_t;
+
+int  VCDSetArea      ( input_thread_t *, input_area_t * );
+void VCDSeek         ( input_thread_t *, off_t );
+int  VCDPlay         ( input_thread_t *, vcdinfo_itemid_t );
diff --git a/modules/access/vcdx/vcdplayer.c b/modules/access/vcdx/vcdplayer.c
new file mode 100644 (file)
index 0000000..f47ddb0
--- /dev/null
@@ -0,0 +1,369 @@
+/*****************************************************************************
+ * vcdplayer.c : VCD input module for vlc
+ *               using libcdio, libvcd and libvcdinfo
+ *****************************************************************************
+ * Copyright (C) 2003 Rocky Bernstein <rocky@panix.com>
+ * $Id: vcdplayer.c,v 1.1 2003/10/04 18:55:13 gbazin Exp $
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+/*
+   This contains more of the vlc-independent parts that might be used
+   in any VCD input module for a media player. However at present there
+   are vlc-specific structures. See also vcdplayer.c of the xine plugin.
+ */
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#include <vlc/vlc.h>
+#include <vlc/input.h>
+
+#include "vcd.h"
+#include "vcdplayer.h"
+
+#include <string.h>
+
+#include <cdio/cdio.h>
+#include <cdio/util.h>
+#include <libvcd/info.h>
+
+/*!
+  Return true if playback control (PBC) is on
+*/
+bool
+vcdplayer_pbc_is_on(const thread_vcd_data_t *p_vcd) 
+{
+  return VCDINFO_INVALID_ENTRY != p_vcd->cur_lid; 
+}
+
+lid_t
+vcdplayer_selection2lid ( input_thread_t *p_input, int entry_num ) 
+{
+  /* FIXME: Some of this probably gets moved to vcdinfo. */
+  /* Convert selection number to lid and then entry number...*/
+  thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
+  unsigned int offset;
+  unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
+  vcdinfo_obj_t *obj = p_vcd->vcd;
+
+  dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
+            "Called lid %u, entry_num %d bsn %d", p_vcd->cur_lid, 
+             entry_num, bsn);
+
+  if ( (entry_num - bsn + 1) > 0) {
+    offset = vcdinfo_lid_get_offset(obj, p_vcd->cur_lid, entry_num-bsn+1);
+  } else {
+    LOG_ERR( "Selection number %u too small. bsn %u", entry_num, bsn );
+    return VCDINFO_INVALID_LID;
+  }
+  
+  if (offset != VCDINFO_INVALID_OFFSET) {
+    vcdinfo_offset_t *ofs;
+    int old = entry_num;
+    
+    switch (offset) {
+    case PSD_OFS_DISABLED:
+      LOG_ERR( "Selection %u disabled", entry_num );
+      return VCDINFO_INVALID_LID;
+    case PSD_OFS_MULTI_DEF:
+      LOG_ERR( "Selection %u multi_def", entry_num );
+      return VCDINFO_INVALID_LID;
+    case PSD_OFS_MULTI_DEF_NO_NUM:
+      LOG_ERR( "Selection %u multi_def_no_num", entry_num );
+      return VCDINFO_INVALID_LID;
+    default: ;
+    }
+    
+    ofs = vcdinfo_get_offset_t(obj, offset);
+
+    if (NULL == ofs) {
+      LOG_ERR( "error in vcdinfo_get_offset" );
+      return -1;
+    }
+    dbg_print(INPUT_DBG_PBC,
+              "entry %u turned into selection lid %u", 
+              old, ofs->lid);
+    return ofs->lid;
+    
+  } else {
+    LOG_ERR( "invalid or unset entry %u", entry_num );
+    return VCDINFO_INVALID_LID;
+  }
+}
+
+static void
+vcdplayer_update_entry( input_thread_t * p_input, uint16_t ofs, 
+                        uint16_t *entry, const char *label)
+{
+  thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
+
+  if ( ofs == VCDINFO_INVALID_OFFSET ) {
+    *entry = VCDINFO_INVALID_ENTRY;
+  } else {
+    vcdinfo_offset_t *off_t = vcdinfo_get_offset_t(p_vcd->vcd, ofs);
+    if (off_t != NULL) {
+      *entry = off_t->lid;
+      dbg_print(INPUT_DBG_PBC, "%s: %d\n", label, off_t->lid);
+    } else
+      *entry = VCDINFO_INVALID_ENTRY;
+  }
+}
+
+/* Handles navigation when NOT in PBC reaching the end of a play item. 
+
+   The navigations rules here may be sort of made up, but the intent 
+   is to do something that's probably right or helpful.
+
+   return true if the caller should return.
+*/
+vcdplayer_read_status_t
+vcdplayer_non_pbc_nav ( input_thread_t * p_input )
+{
+  thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
+
+  /* Not in playback control. Do we advance automatically or stop? */
+  switch (p_vcd->play_item.type) {
+  case VCDINFO_ITEM_TYPE_TRACK:
+  case VCDINFO_ITEM_TYPE_ENTRY: {
+    input_area_t *p_area;
+
+    dbg_print( INPUT_DBG_LSN, "new track %d, lsn %d", p_vcd->cur_track, 
+               p_vcd->p_sectors[p_vcd->cur_track+1] );
+    
+    if ( p_vcd->cur_track >= p_vcd->num_tracks - 1 )
+      return READ_END; /* EOF */
+        
+    p_vcd->play_item.num = p_vcd->cur_track++;
+    
+    vlc_mutex_lock( &p_input->stream.stream_lock );
+    p_area = p_input->stream.pp_areas[p_vcd->cur_track];
+    
+    p_area->i_part = 1;
+    VCDSetArea( p_input, p_area );
+    vlc_mutex_unlock( &p_input->stream.stream_lock );
+    return READ_BLOCK;
+    break;
+  }
+  case VCDINFO_ITEM_TYPE_SPAREID2:  
+    dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
+               "SPAREID2" );
+    /* FIXME */
+    p_input->stream.b_seekable = 0;
+    if (p_vcd->in_still)
+    {
+      return READ_STILL_FRAME ;
+    }
+    return READ_END;
+  case VCDINFO_ITEM_TYPE_NOTFOUND:  
+    LOG_ERR ("NOTFOUND outside PBC -- not supposed to happen");
+    return READ_ERROR;
+  case VCDINFO_ITEM_TYPE_LID:  
+    LOG_ERR ("LID outside PBC -- not supposed to happen");
+    return READ_ERROR;
+  case VCDINFO_ITEM_TYPE_SEGMENT:
+      /* Hack: Just go back and do still again */
+    /* FIXME */
+    p_input->stream.b_seekable = 0;
+    if (p_vcd->in_still) 
+    {
+      dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
+                 "End of Segment - looping" );
+      return READ_STILL_FRAME;
+    }
+    return READ_END;
+  }
+  return READ_BLOCK;
+}
+
+/* FIXME: Will do whatever the right thing is later. */
+#define SLEEP_1_SEC_AND_HANDLE_EVENTS sleep(1)
+
+/* Handles PBC navigation when reaching the end of a play item. */
+vcdplayer_read_status_t
+vcdplayer_pbc_nav ( input_thread_t * p_input )
+{
+  thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
+
+  /* We are in playback control. */
+  vcdinfo_itemid_t itemid;
+
+  if (0 != p_vcd->in_still && p_vcd->in_still != -5) {
+      SLEEP_1_SEC_AND_HANDLE_EVENTS;
+      if (p_vcd->in_still > 0) p_vcd->in_still--;
+      return READ_STILL_FRAME;
+  }
+
+  /* The end of an entry is really the end of the associated 
+     sequence (or track). */
+  
+  if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) && 
+       (p_vcd->cur_lsn < p_vcd->end_lsn) ) {
+    /* Set up to just continue to the next entry */
+    p_vcd->play_item.num++;
+    dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
+               "continuing into next entry: %u", p_vcd->play_item.num);
+    VCDPlay( p_input, p_vcd->play_item );
+    /* p_vcd->update_title(); */
+    return READ_BLOCK;
+  }
+  
+  switch (p_vcd->pxd.descriptor_type) {
+  case PSD_TYPE_END_LIST:
+    return READ_END;
+    break;
+  case PSD_TYPE_PLAY_LIST: {
+    int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
+    
+    dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
+    
+    if (vcdplayer_inc_play_item(p_input))
+      return READ_BLOCK;
+
+    /* Handle any wait time given. */
+    if (-5 == p_vcd->in_still) {
+      if (wait_time != 0) {
+        /* FIXME */
+        p_vcd->in_still = wait_time - 1;
+        SLEEP_1_SEC_AND_HANDLE_EVENTS ;
+        return READ_STILL_FRAME;
+      }
+    }
+    vcdplayer_update_entry( p_input, 
+                            vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
+                            &itemid.num, "next" );
+    itemid.type = VCDINFO_ITEM_TYPE_LID;
+    VCDPlay( p_input, itemid );
+    break;
+  }
+  case PSD_TYPE_SELECTION_LIST:     /* Selection List (+Ext. for SVCD) */
+  case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
+    {
+      int wait_time         = vcdinf_get_timeout_time(p_vcd->pxd.psd);
+      uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
+      uint16_t max_loop     = vcdinf_get_loop_count(p_vcd->pxd.psd);
+      vcdinfo_offset_t *offset_timeout_LID = 
+        vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
+      
+      dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d", 
+                wait_time, p_vcd->loop_count, max_loop);
+      
+      /* Handle any wait time given */
+      if (-5 == p_vcd->in_still) {
+        p_vcd->in_still = wait_time - 1;
+        SLEEP_1_SEC_AND_HANDLE_EVENTS ;
+        return READ_STILL_FRAME;
+      }
+      
+      /* Handle any looping given. */
+      if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
+        p_vcd->loop_count++;
+        if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
+        VCDSeek( p_input, 0 );
+        /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
+        return READ_BLOCK;
+      }
+      
+      /* Looping finished and wait finished. Move to timeout
+         entry or next entry, or handle still. */
+      
+      if (NULL != offset_timeout_LID) {
+        /* Handle timeout_LID */
+        itemid.num  = offset_timeout_LID->lid;
+        itemid.type = VCDINFO_ITEM_TYPE_LID;
+        dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
+        VCDPlay( p_input, itemid );
+        return READ_BLOCK;
+      } else {
+        int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
+        if (num_selections > 0) {
+          /* Pick a random selection. */
+          unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
+          int rand_selection=bsn +
+            (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
+          lid_t rand_lid=vcdplayer_selection2lid (p_input, rand_selection);
+          itemid.num = rand_lid;
+          itemid.type = VCDINFO_ITEM_TYPE_LID;
+          dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d", 
+                    rand_selection - bsn, rand_lid);
+          VCDPlay( p_input, itemid );
+          return READ_BLOCK;
+        } else if (p_vcd->in_still) {
+          /* Hack: Just go back and do still again */
+          SLEEP_1_SEC_AND_HANDLE_EVENTS ;
+          return READ_STILL_FRAME;
+        }
+      }
+      break;
+    }
+  case VCDINFO_ITEM_TYPE_NOTFOUND:  
+    LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
+    break;
+  case VCDINFO_ITEM_TYPE_SPAREID2:  
+    LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
+    break;
+  case VCDINFO_ITEM_TYPE_LID:  
+    LOG_ERR( "LID in PBC -- not supposed to happen" );
+    break;
+    
+  default:
+    ;
+  }
+  /* FIXME: Should handle autowait ...  */
+
+  return READ_ERROR;
+}
+
+/*
+  Get the next play-item in the list given in the LIDs. Note play-item
+  here refers to list of play-items for a single LID It shouldn't be
+  confused with a user's list of favorite things to play or the 
+  "next" field of a LID which moves us to a different LID.
+ */
+bool
+vcdplayer_inc_play_item( input_thread_t *p_input )
+{
+  thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
+
+  int noi;
+
+  dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
+
+  if ( NULL == p_vcd || NULL == p_vcd->pxd.pld  ) return false;
+
+  noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
+  
+  if ( noi <= 0 ) return false;
+  
+  /* Handle delays like autowait or wait here? */
+
+  p_vcd->pdi++;
+
+  if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return false;
+
+  else {
+    uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld, 
+                                                       p_vcd->pdi);
+    vcdinfo_itemid_t trans_itemid;
+
+    if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false;
+    
+    vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
+    dbg_print(INPUT_DBG_PBC, "  play-item[%d]: %s",
+              p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
+    return VLC_SUCCESS == VCDPlay( p_input, trans_itemid );
+  }
+}
diff --git a/modules/access/vcdx/vcdplayer.h b/modules/access/vcdx/vcdplayer.h
new file mode 100644 (file)
index 0000000..aae929c
--- /dev/null
@@ -0,0 +1,117 @@
+/*****************************************************************************
+ * Copyright (C) 2003 Rocky Bernstein (for VideoLAN)
+ * $Id: vcdplayer.h,v 1.1 2003/10/04 18:55:13 gbazin Exp $
+ *
+ * Authors: Rocky Bernstein <rocky@panix.com> 
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+/* VCD Player header. More or less media-player independent */
+
+#ifndef _VCDPLAYER_H_
+#define _VCDPLAYER_H_
+
+#include <libvcd/info.h>
+
+#define INPUT_DBG_MRL         1 
+#define INPUT_DBG_EXT         2 /* Calls from external routines */
+#define INPUT_DBG_CALL        4 /* all calls */
+#define INPUT_DBG_LSN         8 /* LSN changes */
+#define INPUT_DBG_PBC        16 /* Playback control */
+#define INPUT_DBG_CDIO       32 /* Debugging from CDIO */
+#define INPUT_DBG_SEEK       64 /* Seeks to set location */
+#define INPUT_DBG_STILL     128 /* Still-frame */
+#define INPUT_DBG_VCDINFO   256 /* Debugging from VCDINFO */
+
+#define INPUT_DEBUG 1
+#if INPUT_DEBUG
+#define dbg_print(mask, s, args...) \
+   if (p_vcd->i_debug & mask) \
+     msg_Dbg(p_input, "%s: "s, __func__ , ##args)
+#else
+#define dbg_print(mask, s, args...) 
+#endif
+
+#define LOG_ERR(args...) msg_Err( p_input, args )
+
+/* vcdplayer_read return status */
+typedef enum {
+  READ_BLOCK,
+  READ_STILL_FRAME,
+  READ_ERROR,
+  READ_END,
+} vcdplayer_read_status_t;
+
+/*****************************************************************************
+ * thread_vcd_data_t: VCD information
+ *****************************************************************************/
+typedef struct thread_vcd_data_s
+{
+  vcdinfo_obj_t *vcd;                   /* CD device descriptor */
+  int            in_still;              /*  0 if not in still, 
+                                            -2 if in infinite loop
+                                            -5 if a still but haven't 
+                                            read wait time yet
+                                            >0 number of seconds yet to 
+                                            wait */
+  unsigned int num_tracks;              /* Nb of tracks (titles) */
+  unsigned int num_segments;            /* Nb of segments */
+  unsigned int num_entries;             /* Nb of entries */
+  unsigned int num_lids;                /* Nb of List IDs */
+  vcdinfo_itemid_t play_item;           /* play-item, VCDPLAYER_BAD_ENTRY 
+                                           if none */
+  int          cur_lid;                 /* LID that play item is in. Implies 
+                                           PBC is on. VCDPLAYER_BAD_ENTRY if 
+                                           not none or not in PBC */
+  PsdListDescriptor pxd;                /* If PBC is on, the relevant 
+                                           PSD/PLD */
+  int          pdi;                     /* current pld index of pxd. -1 if 
+                                           no index*/
+  vcdinfo_itemid_t loop_item;           /* Where do we loop back to? 
+                                           Meaningful only in a selection 
+                                           list */
+  int          loop_count;              /* # of times play-item has been 
+                                           played. Meaningful only in a 
+                                           selection list.              */
+  track_t      cur_track;               /* Current track number */
+  lsn_t        cur_lsn;                 /* Current logical sector number */
+  lsn_t        end_lsn;                 /* LSN of end of current 
+                                           entry/segment/track. */
+  lsn_t        origin_lsn;              /* LSN of start of seek/slider */
+  lsn_t *      p_sectors;               /* Track sectors */
+  lsn_t *      p_entries;               /* Entry points */
+  lsn_t *      p_segments;              /* Segments */
+  bool         b_valid_ep;              /* Valid entry points flag */
+  vlc_bool_t   b_end_of_track;          /* If the end of track was reached */
+  int          i_debug;                 /* Debugging mask */
+  
+} thread_vcd_data_t;
+
+bool  vcdplayer_inc_play_item( input_thread_t *p_input );
+bool  vcdplayer_pbc_is_on(const thread_vcd_data_t *p_this);
+
+vcdplayer_read_status_t vcdplayer_pbc_nav ( input_thread_t * p_input );
+vcdplayer_read_status_t vcdplayer_non_pbc_nav ( input_thread_t * p_input );
+lid_t vcdplayer_selection2lid ( input_thread_t *p_input, int entry_num ) ;
+
+#endif /* _VCDPLAYER_H_ */
+/* 
+ * Local variables:
+ *  c-file-style: "gnu"
+ *  tab-width: 8
+ *  indent-tabs-mode: nil
+ * End:
+ */