diff --git a/configure.ac b/configure.ac index 0b7525a7f46457632ff834a40e6f035827be8347..887c788d192961587c2b8cb194e30fd27071eacd 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/modules/LIST b/modules/LIST index 4d4c8e759e64721069fb63a50f03896f9204b9b7..7c9666c19e4e31d1facccc7f5b0b699b57e7d3c8 100644 --- a/modules/LIST +++ b/modules/LIST @@ -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. diff --git a/modules/access/Modules.am b/modules/access/Modules.am index 32f39d8574273f24ba61afef2f2f680cf026b202..686321f14e4ea90161101281ce4eee301793b94c 100644 --- a/modules/access/Modules.am +++ b/modules/access/Modules.am @@ -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 index 0000000000000000000000000000000000000000..371b8c8f76eb3ea8811fe02c33d4c8b187c7bc93 --- /dev/null +++ b/modules/access/cddax.c @@ -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 + * Gildas Bazin + * Rocky Bernstein + * + * 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 +#include + +#include +#include +#include +#include +#include + +#include "codecs.h" + +#ifdef HAVE_UNISTD_H +# include +#endif + +#include + +#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 index 0000000000000000000000000000000000000000..d0721f647c1e4a1207bec058780bc0e8bd7e1769 --- /dev/null +++ b/modules/access/vcdx/.cvsignore @@ -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 index 0000000000000000000000000000000000000000..c9dc7343ef6ee32719a4a5249094797897b256d8 --- /dev/null +++ b/modules/access/vcdx/Modules.am @@ -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 index 0000000000000000000000000000000000000000..bc99a73226618a89c92dc247269954699c87760c --- /dev/null +++ b/modules/access/vcdx/access.c @@ -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 + * Rocky Bernstein + * + * 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 +#include + +#include "../../demux/mpeg/system.h" +#include "vcd.h" +#include "intf.h" +#include "vcdplayer.h" + +#include +#include +#include +#include +#include + +#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 index 0000000000000000000000000000000000000000..95cf42e2651d993fa18976601e46995692ee0df1 --- /dev/null +++ b/modules/access/vcdx/cdrom.c @@ -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 + * Gildas Bazin + * Jon Lech Johansen + * + * 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 +#include + +#include + +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include + +#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 index 0000000000000000000000000000000000000000..9c198afce00eb2f0bba9fd3eee11bdcef58d5d7b --- /dev/null +++ b/modules/access/vcdx/cdrom.h @@ -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 + * Gildas Bazin + * + * 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 +#include + +/***************************************************************************** + * 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 index 0000000000000000000000000000000000000000..1c23f871230d191b99ea3458898c87f6750eb455 --- /dev/null +++ b/modules/access/vcdx/demux.c @@ -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 + * + * 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 +#include +#include + +#include +#include +#include + +#include "../../demux/mpeg/system.h" + +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include + +#ifdef STRNCASECMP_IN_STRINGS_H +# include +#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 index 0000000000000000000000000000000000000000..cf309e2b35d162f6dde2f62c6d023c64ba1c224d --- /dev/null +++ b/modules/access/vcdx/intf.c @@ -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 + * 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 /* malloc(), free() */ +#include +#include + +#include +#include + +#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 index 0000000000000000000000000000000000000000..fb72e11e77a3d17c53fcc16f2e6abe50ded41097 --- /dev/null +++ b/modules/access/vcdx/intf.h @@ -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 + * + * 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 index 0000000000000000000000000000000000000000..8567ee8dcd8ea73b558f44d7bcdafea990d80126 --- /dev/null +++ b/modules/access/vcdx/vcd.c @@ -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 + * Rocky Bernstein + * + * 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 +#include + +#include "../../demux/mpeg/system.h" +#include "vcd.h" +#include "intf.h" +#include "vcdplayer.h" + +#include +#include +#include +#include +#include +#include + +#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 index 0000000000000000000000000000000000000000..fc6fad6dc2ddc2a995d3b3812dcf40b08c3ea4f8 --- /dev/null +++ b/modules/access/vcdx/vcd.h @@ -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 + * + * 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 + +/***************************************************************************** + * 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 index 0000000000000000000000000000000000000000..f47ddb00fa40a55fa3115b0915fe5c50017fa6b6 --- /dev/null +++ b/modules/access/vcdx/vcdplayer.c @@ -0,0 +1,369 @@ +/***************************************************************************** + * vcdplayer.c : VCD input module for vlc + * using libcdio, libvcd and libvcdinfo + ***************************************************************************** + * Copyright (C) 2003 Rocky Bernstein + * $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 +#include + +#include "vcd.h" +#include "vcdplayer.h" + +#include + +#include +#include +#include + +/*! + 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 index 0000000000000000000000000000000000000000..aae929cb1eb2c7d7a99cbd31b426d3d38f7b8356 --- /dev/null +++ b/modules/access/vcdx/vcdplayer.h @@ -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 + * + * 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 + +#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: + */