Commit fd05dc69 authored by Gildas Bazin's avatar Gildas Bazin

* modules/access/vcdx/*: Brand new VCD input module using libcdio, libvcd and libvcdinfo. Courtesy of Rocky Bernstein.
* modules/access/cddax.c: Brand new CDDA input module using libcdio. Courtesy of Rocky Bernstein.
* modules/LIST: update.
parent 0f814562
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
......
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.
......
......@@ -10,3 +10,7 @@ SOURCES_cdda = \
vcd/cdrom.h \
vcd/cdrom_internals.h \
$(NULL)
SOURCES_cddax = \
cddax.c \
vcdx/cdrom.c \
vcdx/cdrom.h
/*****************************************************************************
* cddax.c : CD digital audio input module for vlc using libcdio
*****************************************************************************
* Copyright (C) 2000 VideoLAN
* $Id: cddax.c,v 1.1 2003/10/04 18:55:13 gbazin Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Gildas Bazin <gbazin@netcourrier.com>
* Rocky Bernstein <rocky@panix.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <sys/types.h>
#include <cdio/cdio.h>
#include <cdio/cd_types.h>
#include "codecs.h"
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <string.h>
#include "vcdx/cdrom.h"
/* how many blocks CDDAOpen will read in each loop */
#define CDDA_BLOCKS_ONCE 1
#define CDDA_DATA_ONCE (CDDA_BLOCKS_ONCE * CDIO_CD_FRAMESIZE_RAW)
/*****************************************************************************
* cdda_data_t: CD audio information
*****************************************************************************/
typedef struct cdda_data_s
{
cddev_t *p_cddev; /* CD device descriptor */
int i_nb_tracks; /* Nb of tracks (titles) */
int i_track; /* Current track */
lsn_t i_sector; /* Current Sector */
lsn_t * p_sectors; /* Track sectors */
vlc_bool_t b_end_of_track; /* If the end of track was reached */
} cdda_data_t;
struct demux_sys_t
{
es_descriptor_t *p_es;
mtime_t i_pts;
};
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int CDDAOpen ( vlc_object_t * );
static void CDDAClose ( vlc_object_t * );
static int CDDARead ( input_thread_t *, byte_t *, size_t );
static void CDDASeek ( input_thread_t *, off_t );
static int CDDASetArea ( input_thread_t *, input_area_t * );
static int CDDASetProgram ( input_thread_t *, pgrm_descriptor_t * );
static int CDDAOpenDemux ( vlc_object_t * );
static void CDDACloseDemux ( vlc_object_t * );
static int CDDADemux ( input_thread_t * p_input );
/*****************************************************************************
* Module descriptior
*****************************************************************************/
#define CACHING_TEXT N_("Caching value in ms")
#define CACHING_LONGTEXT N_( \
"Allows you to modify the default caching value for cdda streams. This " \
"value should be set in miliseconds units." )
vlc_module_begin();
set_description( _("CD Audio input") );
set_capability( "access", 75 /* slightly higher than cdda */ );
add_integer( "cddax-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
set_callbacks( CDDAOpen, CDDAClose );
add_shortcut( "cdda" );
add_submodule();
set_description( _("CD Audio demux") );
set_capability( "demux", 0 );
set_callbacks( CDDAOpenDemux, CDDACloseDemux );
add_shortcut( "cdda" );
vlc_module_end();
/*****************************************************************************
* CDDAOpen: open cdda
*****************************************************************************/
static int CDDAOpen( vlc_object_t *p_this )
{
input_thread_t * p_input = (input_thread_t *)p_this;
char * psz_orig;
char * psz_parser;
char * psz_source;
cdda_data_t * p_cdda;
int i;
input_area_t * p_area;
int i_title = 1;
cddev_t *p_cddev;
/* parse the options passed in command line : */
psz_orig = psz_parser = psz_source = strdup( p_input->psz_name );
if( !psz_orig )
{
return( -1 );
}
while( *psz_parser && *psz_parser != '@' )
{
psz_parser++;
}
if( *psz_parser == '@' )
{
/* Found options */
*psz_parser = '\0';
++psz_parser;
if ('T' == *psz_parser || 't' == *psz_parser )
++psz_parser;
i_title = (int)strtol( psz_parser, NULL, 10 );
i_title = i_title ? i_title : 1;
}
if( !*psz_source ) {
/* No source specified, so figure it out. */
if( !p_input->psz_access ) {
free( psz_orig );
return -1;
}
psz_source = config_GetPsz( p_input, MODULE_STRING "-device" );
if( !psz_source ) {
/* Scan for a CD with a VCD in it. */
char **cd_drives =
cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false);
if (NULL == cd_drives) return -1;
if (cd_drives[0] == NULL) {
cdio_free_device_list(cd_drives);
return -1;
}
psz_source = strdup(cd_drives[0]);
cdio_free_device_list(cd_drives);
}
}
/* Open CDDA */
if( !(p_cddev = ioctl_Open( p_this, psz_source )) )
{
msg_Warn( p_input, "could not open %s", psz_source );
free( psz_source );
return -1;
}
free( psz_source );
p_cdda = malloc( sizeof(cdda_data_t) );
if( p_cdda == NULL )
{
msg_Err( p_input, "out of memory" );
free( psz_source );
return -1;
}
p_cdda->p_cddev = p_cddev;
p_input->p_access_data = (void *)p_cdda;
p_input->i_mtu = CDDA_DATA_ONCE;
/* We read the Table Of Content information */
p_cdda->i_nb_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
p_cdda->p_cddev->cdio, &p_cdda->p_sectors );
if( p_cdda->i_nb_tracks < 0 )
msg_Err( p_input, "unable to count tracks" );
else if( p_cdda->i_nb_tracks <= 0 )
msg_Err( p_input, "no audio tracks found" );
if( p_cdda->i_nb_tracks <= 1)
{
ioctl_Close( p_cdda->p_cddev );
free( p_cdda );
return -1;
}
if( i_title >= p_cdda->i_nb_tracks || i_title < 1 )
i_title = 1;
/* Set stream and area data */
vlc_mutex_lock( &p_input->stream.stream_lock );
/* Initialize ES structures */
input_InitStream( p_input, 0 );
/* cdda input method */
p_input->stream.i_method = INPUT_METHOD_CDDA;
p_input->stream.b_pace_control = 1;
p_input->stream.b_seekable = 1;
p_input->stream.i_mux_rate = 44100 * 4 / 50;
#define area p_input->stream.pp_areas
for( i = 1 ; i <= p_cdda->i_nb_tracks ; i++ )
{
input_AddArea( p_input, i, 1 );
/* Absolute start offset and size */
area[i]->i_start =
(off_t)p_cdda->p_sectors[i-1] * (off_t)CDIO_CD_FRAMESIZE_RAW;
area[i]->i_size =
(off_t)(p_cdda->p_sectors[i] - p_cdda->p_sectors[i-1])
* (off_t)CDIO_CD_FRAMESIZE_RAW;
}
#undef area
p_area = p_input->stream.pp_areas[i_title];
CDDASetArea( p_input, p_area );
vlc_mutex_unlock( &p_input->stream.stream_lock );
if( !p_input->psz_demux || !*p_input->psz_demux )
{
p_input->psz_demux = "cdda";
}
p_input->pf_read = CDDARead;
p_input->pf_seek = CDDASeek;
p_input->pf_set_area = CDDASetArea;
p_input->pf_set_program = CDDASetProgram;
/* Update default_pts to a suitable value for cdda access */
p_input->i_pts_delay = config_GetInt( p_input, "cddax-caching" ) * 1000;
return 0;
}
/*****************************************************************************
* CDDAClose: closes cdda
*****************************************************************************/
static void CDDAClose( vlc_object_t *p_this )
{
input_thread_t * p_input = (input_thread_t *)p_this;
cdda_data_t *p_cdda = (cdda_data_t *)p_input->p_access_data;
ioctl_Close( p_cdda->p_cddev );
free( p_cdda );
}
/*****************************************************************************
* CDDARead: reads from the CDDA into PES packets.
*****************************************************************************
* Returns -1 in case of error, 0 in case of EOF, otherwise the number of
* bytes.
*****************************************************************************/
static int CDDARead( input_thread_t * p_input, byte_t * p_buffer,
size_t i_len )
{
cdda_data_t * p_cdda;
int i_blocks;
int i_index;
int i_read;
p_cdda = (cdda_data_t *)p_input->p_access_data;
i_read = 0;
/* Compute the number of blocks we have to read */
i_blocks = i_len / CDIO_CD_FRAMESIZE_RAW;
for ( i_index = 0; i_index < i_blocks; i_index++ )
{
if (cdio_read_audio_sector(p_cdda->p_cddev->cdio, p_buffer,
p_cdda->i_sector) != 0)
{
msg_Err( p_input, "could not read sector %d", p_cdda->i_sector );
return -1;
}
p_cdda->i_sector ++;
if ( p_cdda->i_sector == p_cdda->p_sectors[p_cdda->i_track + 1] )
{
input_area_t *p_area;
if ( p_cdda->i_track >= p_cdda->i_nb_tracks - 1 )
return 0; /* EOF */
vlc_mutex_lock( &p_input->stream.stream_lock );
p_area = p_input->stream.pp_areas[
p_input->stream.p_selected_area->i_id + 1 ];
msg_Dbg( p_input, "new title" );
p_area->i_part = 1;
CDDASetArea( p_input, p_area );
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
i_read += CDIO_CD_FRAMESIZE_RAW;
}
if ( i_len % CDIO_CD_FRAMESIZE_RAW ) /* this should not happen */
{
msg_Err( p_input, "must read full sectors" );
}
return i_read;
}
/*****************************************************************************
* CDDASetProgram: Does nothing since a CDDA is mono_program
*****************************************************************************/
static int CDDASetProgram( input_thread_t * p_input,
pgrm_descriptor_t * p_program)
{
return 0;
}
/*****************************************************************************
* CDDASetArea: initialize input data for title x.
* It should be called for each user navigation request.
****************************************************************************/
static int CDDASetArea( input_thread_t * p_input, input_area_t * p_area )
{
cdda_data_t *p_cdda = (cdda_data_t*)p_input->p_access_data;
vlc_value_t val;
/* we can't use the interface slider until initilization is complete */
p_input->stream.b_seekable = 0;
if( p_area != p_input->stream.p_selected_area )
{
/* Change the default area */
p_input->stream.p_selected_area = p_area;
/* Change the current track */
p_cdda->i_track = p_area->i_id - 1;
p_cdda->i_sector = p_cdda->p_sectors[p_cdda->i_track];
/* Update the navigation variables without triggering a callback */
val.i_int = p_area->i_id;
var_Change( p_input, "title", VLC_VAR_SETVALUE, &val, NULL );
}
p_cdda->i_sector = p_cdda->p_sectors[p_cdda->i_track];
p_input->stream.p_selected_area->i_tell =
(off_t)p_cdda->i_sector * (off_t)CDIO_CD_FRAMESIZE_RAW
- p_input->stream.p_selected_area->i_start;
/* warn interface that something has changed */
p_input->stream.b_seekable = 1;
p_input->stream.b_changed = 1;
return 0;
}
/****************************************************************************
* CDDASeek
****************************************************************************/
static void CDDASeek( input_thread_t * p_input, off_t i_off )
{
cdda_data_t * p_cdda;
p_cdda = (cdda_data_t *) p_input->p_access_data;
p_cdda->i_sector = p_cdda->p_sectors[p_cdda->i_track]
+ i_off / (off_t)CDIO_CD_FRAMESIZE_RAW;
vlc_mutex_lock( &p_input->stream.stream_lock );
p_input->stream.p_selected_area->i_tell =
(off_t)p_cdda->i_sector * (off_t)CDIO_CD_FRAMESIZE_RAW
- p_input->stream.p_selected_area->i_start;
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
/****************************************************************************
* Demux Part
****************************************************************************/
static int CDDAOpenDemux ( vlc_object_t * p_this)
{
input_thread_t *p_input = (input_thread_t *)p_this;
demux_sys_t *p_demux;
WAVEFORMATEX *p_wf;
if( p_input->stream.i_method != INPUT_METHOD_CDDA )
{
return VLC_EGENERIC;
}
p_demux = malloc( sizeof( es_descriptor_t ) );
p_demux->i_pts = 0;
p_demux->p_es = NULL;
p_input->pf_demux = CDDADemux;
p_input->pf_rewind = NULL;
p_input->p_demux_data = p_demux;
vlc_mutex_lock( &p_input->stream.stream_lock );
if( input_AddProgram( p_input, 0, 0) == NULL )
{
msg_Err( p_input, "cannot add program" );
free( p_input->p_demux_data );
return( -1 );
}
p_input->stream.pp_programs[0]->b_is_ok = 0;
p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
/* create our ES */
p_demux->p_es = input_AddES( p_input,
p_input->stream.p_selected_program,
1 /* id */, AUDIO_ES, NULL, 0 );
if( !p_demux->p_es )
{
vlc_mutex_unlock( &p_input->stream.stream_lock );
msg_Err( p_input, "out of memory" );
free( p_input->p_demux_data );
return( -1 );
}
p_demux->p_es->i_stream_id = 1;
p_demux->p_es->i_fourcc = VLC_FOURCC('a','r','a','w');
p_demux->p_es->p_waveformatex = p_wf = malloc( sizeof( WAVEFORMATEX ) );
p_wf->wFormatTag = WAVE_FORMAT_PCM;
p_wf->nChannels = 2;
p_wf->nSamplesPerSec = 44100;
p_wf->nAvgBytesPerSec = 2 * 44100 * 2;
p_wf->nBlockAlign = 4;
p_wf->wBitsPerSample = 16;
p_wf->cbSize = 0;
input_SelectES( p_input, p_demux->p_es );
p_input->stream.p_selected_program->b_is_ok = 1;
vlc_mutex_unlock( &p_input->stream.stream_lock );
return VLC_SUCCESS;
}
static void CDDACloseDemux( vlc_object_t * p_this)
{
input_thread_t *p_input = (input_thread_t*)p_this;
demux_sys_t *p_demux = (demux_sys_t*)p_input->p_demux_data;
free( p_demux );
p_input->p_demux_data = NULL;
return;
}
static int CDDADemux( input_thread_t * p_input )
{
demux_sys_t *p_demux = (demux_sys_t*)p_input->p_demux_data;
ssize_t i_read;
data_packet_t * p_data;
pes_packet_t * p_pes;
input_ClockManageRef( p_input,
p_input->stream.p_selected_program,
p_demux->i_pts );
i_read = input_SplitBuffer( p_input, &p_data, CDIO_CD_FRAMESIZE_RAW );
if( i_read <= 0 )
{
return 0; // EOF
}
p_pes = input_NewPES( p_input->p_method_data );
if( p_pes == NULL )
{
msg_Err( p_input, "out of memory" );
input_DeletePacket( p_input->p_method_data, p_data );
return -1;
}
p_pes->i_rate = p_input->stream.control.i_rate;
p_pes->p_first = p_pes->p_last = p_data;
p_pes->i_nb_data = 1;
p_pes->i_pes_size = i_read;
p_pes->i_dts =
p_pes->i_pts = input_ClockGetTS( p_input,
p_input->stream.p_selected_program,
p_demux->i_pts );
if( p_demux->p_es->p_decoder_fifo )
{
input_DecodePES( p_demux->p_es->p_decoder_fifo, p_pes );
}
else
{
input_DeletePES( p_input->p_method_data, p_pes );
}
p_demux->i_pts += ((mtime_t)90000) * i_read
/ (mtime_t)44100 / 4 /* stereo 16 bits */;
return 1;
}
.deps
.dirstamp
Makefile
Makefile.am
Makefile.in
*.dll
*.dylib
*.sl
*.so
SOURCES_vcdx = \
intf.c \
intf.h \
vcd.c \
vcd.h \
vcdplayer.h \
vcdplayer.c \
cdrom.c \
cdrom.h \
demux.c \
access.c \
$(NULL)
/*****************************************************************************
* vcd.c : VCD input module for vlc
* using libcdio, libvcd and libvcdinfo. vlc-specific things tend
* to go here.
*****************************************************************************
* Copyright (C) 2000 VideoLAN
* $Id: access.c,v 1.1 2003/10/04 18:55:13 gbazin Exp $
*
* Authors: Johan Bilien <jobi@via.ecp.fr>
* Rocky Bernstein <rocky@panix.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#if 0 // Disabled until this is working
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "../../demux/mpeg/system.h"
#include "vcd.h"
#include "intf.h"
#include "vcdplayer.h"
#include <cdio/cdio.h>
#include <cdio/logging.h>
#include <cdio/util.h>
#include <libvcd/info.h>
#include <libvcd/logging.h>
#include "cdrom.h"
/* how many blocks VCDRead will read in each loop */
#define VCD_BLOCKS_ONCE 20
#define VCD_DATA_ONCE (VCD_BLOCKS_ONCE * M2F2_SECTOR_SIZE)
/*****************************************************************************
* Local prototypes
*****************************************************************************/
/* First those which are accessed from outside (via pointers). */
static int VCDOpen ( vlc_object_t * );
static void VCDClose ( vlc_object_t * );
static int VCDRead ( input_thread_t *, byte_t *, size_t );
static int VCDRead ( input_thread_t *, byte_t *, size_t );
static int VCDSetProgram ( input_thread_t *, pgrm_descriptor_t * );
/* Now those which are strictly internal */
static void VCDSetOrigin ( input_thread_t *, lsn_t origin_lsn,
lsn_t cur_lsn, lsn_t end_lsn,
int cur_entry, track_t track );
static int VCDEntryPoints ( input_thread_t * );
static int VCDLIDs ( input_thread_t * );
static int VCDSegments ( input_thread_t * );
static void VCDTracks ( input_thread_t * );
static int VCDReadSector ( vlc_object_t *p_this,
const vcdinfo_obj_t *p_vcd, lsn_t cur_lsn,
byte_t * p_buffer );
static char *VCDParse ( input_thread_t *,
/*out*/ vcdinfo_itemid_t * p_itemid );
static void VCDUpdateVar( input_thread_t *p_input, int i_entry, int i_action,
const char *varname, const char *label );
static vcdinfo_obj_t *vcd_Open ( vlc_object_t *p_this, const char *psz_dev );
static int debug_callback ( vlc_object_t *p_this, const char *psz_name,
vlc_value_t oldval, vlc_value_t val,
void *p_data );
#define DEBUG_TEXT N_("set debug mask for additional debugging.")
#define DEBUG_LONGTEXT N_( \
"This integer when viewed in binary is a debugging mask\n" \
"MRL 1\n" \
"external call 2\n" \
"all calls 4\n" \
"LSN 8\n" \
"PBC (10) 16\n" \
"libcdio (20) 32\n" \
"seeks (40) 64\n" \
"still (80) 128\n" \
"vcdinfo (100) 256\n" )
/****************************************************************************
* Private functions
****************************************************************************/
/* FIXME: This variable is a hack. Would be nice to eliminate the
global-ness. */
static input_thread_t *p_vcd_input = NULL;
int
vcd_debug_callback ( vlc_object_t *p_this, const char *psz_name,
vlc_value_t oldval, vlc_value_t val, void *p_data )
{
thread_vcd_data_t *p_vcd;
if (NULL == p_vcd_input) return VLC_EGENERIC;
p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d",
p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
}
p_vcd->i_debug = val.i_int;
return VLC_SUCCESS;
}
/* process messages that originate from libcdio. */
static void
cdio_log_handler (cdio_log_level_t level, const char message[])
{
thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
switch (level) {
case CDIO_LOG_DEBUG:
case CDIO_LOG_INFO:
if (p_vcd->i_debug & INPUT_DBG_CDIO)
msg_Dbg( p_vcd_input, message);
break;
case CDIO_LOG_WARN:
msg_Warn( p_vcd_input, message);
break;
case CDIO_LOG_ERROR:
case CDIO_LOG_ASSERT:
msg_Err( p_vcd_input, message);
break;
default:
msg_Warn( p_vcd_input, message,
_("The above message had unknown vcdimager log level"),
level);
}
return;
}
/* process messages that originate from vcdinfo. */
static void
vcd_log_handler (vcd_log_level_t level, const char message[])
{
thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
switch (level) {
case VCD_LOG_DEBUG:
case VCD_LOG_INFO:
if (p_vcd->i_debug & INPUT_DBG_VCDINFO)
msg_Dbg( p_vcd_input, message);
break;
case VCD_LOG_WARN:
msg_Warn( p_vcd_input, message);
break;
case VCD_LOG_ERROR:
case VCD_LOG_ASSERT:
msg_Err( p_vcd_input, message);
break;
default:
msg_Warn( p_vcd_input, "%s\n%s %d", message,
_("The above message had unknown vcdimager log level"),
level);
}
return;
}
/*
* Data reading functions
*/
/*****************************************************************************
VCDOpen: open VCD.
read in meta-information about VCD: the number of tracks, segments,
entries, size and starting information. Then set up state variables so
that we read/seek starting at the location specified.
On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
and VLC_EGENERIC for some other error.
*****************************************************************************/
static int
VCDOpen( vlc_object_t *p_this )
{
input_thread_t * p_input = (input_thread_t *)p_this;
thread_vcd_data_t * p_vcd;
char * psz_source;
vcdinfo_itemid_t itemid;
bool play_ok;
p_input->pf_read = VCDRead;
p_input->pf_seek = VCDSeek;
p_input->pf_set_area = VCDSetArea;
p_input->pf_set_program = VCDSetProgram;
p_vcd = malloc( sizeof(thread_vcd_data_t) );
if( p_vcd == NULL )
{
LOG_ERR ("out of memory" );
return VLC_ENOMEM;
}
p_input->p_access_data = (void *)p_vcd;
p_vcd->i_debug = config_GetInt( p_this, MODULE_STRING "-debug" );
psz_source = VCDParse( p_input, &itemid );
if ( NULL == psz_source )
{
free( p_vcd );
return( VLC_EGENERIC );
}
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "%s", psz_source );
p_vcd->p_segments = NULL;
p_vcd->p_entries = NULL;
/* set up input */
p_input->i_mtu = VCD_DATA_ONCE;
vlc_mutex_lock( &p_input->stream.stream_lock );
/* If we are here we can control the pace... */
p_input->stream.b_pace_control = 1;
p_input->stream.b_seekable = 1;
p_input->stream.p_selected_area->i_size = 0;
p_input->stream.p_selected_area->i_tell = 0;
vlc_mutex_unlock( &p_input->stream.stream_lock );
if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
{
msg_Warn( p_input, "could not open %s", psz_source );
free( psz_source );
free( p_vcd );
return VLC_EGENERIC;
}
/* Get track information. */
p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
vcdinfo_get_cd_image(p_vcd->vcd),
&p_vcd->p_sectors );
free( psz_source );
if( p_vcd->num_tracks < 0 )
LOG_ERR ("unable to count tracks" );
else if( p_vcd->num_tracks <= 1 )
LOG_ERR ("no movie tracks found" );
if( p_vcd->num_tracks <= 1)
{
vcdinfo_close( p_vcd->vcd );
free( p_vcd );
return VLC_EGENERIC;
}
/* Set stream and area data */
vlc_mutex_lock( &p_input->stream.stream_lock );
/* Initialize ES structures */
input_InitStream( p_input, sizeof( stream_ps_data_t ) );
/* disc input method */
p_input->stream.i_method = INPUT_METHOD_VCD;
p_input->stream.i_area_nb = 1;
/* Initialize segment information. */
VCDSegments( p_input );
/* Initialize track area information. */
VCDTracks( p_input );
if( VCDEntryPoints( p_input ) < 0 )
{
msg_Warn( p_input, "could not read entry points, will not use them" );
p_vcd->b_valid_ep = false;
}
if( VCDLIDs( p_input ) < 0 )
{
msg_Warn( p_input, "could not read entry LIDs" );
}
play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
vlc_mutex_unlock( &p_input->stream.stream_lock );
if ( ! play_ok ) {
vcdinfo_close( p_vcd->vcd );
free( p_vcd );
return VLC_EGENERIC;
}
if( !p_input->psz_demux || !*p_input->psz_demux )
{
#if FIXED
p_input->psz_demux = "vcdx";
#else
p_input->psz_demux = "ps";
#endif
}
return VLC_SUCCESS;
}
/*****************************************************************************
* VCDClose: closes VCD releasing allocated memory.
*****************************************************************************/
static void
VCDClose( vlc_object_t *p_this )
{
input_thread_t * p_input = (input_thread_t *)p_this;
thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
vcdinfo_close( p_vcd->vcd );
free( p_vcd->p_entries );
free( p_vcd->p_segments );
free( p_vcd );
p_vcd_input = NULL;
}
/*****************************************************************************
* VCDRead: reads i_len bytes from the VCD into p_buffer.
*****************************************************************************
* Returns -1 in case of error, 0 in case of EOF, otherwise the number of
* bytes.
*****************************************************************************/
static int
VCDRead( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
{
thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
int i_blocks;
int i_index;
int i_read;
byte_t p_last_sector[ M2F2_SECTOR_SIZE ];
i_read = 0;
/* Compute the number of blocks we have to read */
i_blocks = i_len / M2F2_SECTOR_SIZE;
for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
{
if ( p_vcd->cur_lsn == p_vcd->end_lsn ) {
vcdplayer_read_status_t read_status;
/* We've run off of the end of this entry. Do we continue or stop? */
dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
"end reached, cur: %u", p_vcd->cur_lsn );
read_status = vcdplayer_pbc_is_on( p_vcd )
? vcdplayer_pbc_nav( p_input )
: vcdplayer_non_pbc_nav( p_input );
switch (read_status) {
case READ_END:
/* End reached. Return NULL to indicated this. */
case READ_ERROR:
/* Some sort of error. */
return i_read;
case READ_STILL_FRAME:
{
byte_t * p_buf = p_buffer;
p_buf += (i_index*M2F2_SECTOR_SIZE);
memset(p_buf, 0, M2F2_SECTOR_SIZE);
p_buf += 2;
*p_buf = 0x01;
dbg_print(INPUT_DBG_STILL, "Handled still event\n");
return i_read + M2F2_SECTOR_SIZE;
}
default:
case READ_BLOCK:
break;
}
}
if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
p_vcd->cur_lsn,
p_buffer + (i_index*M2F2_SECTOR_SIZE) ) < 0 )
{
LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
return -1;
}
p_vcd->cur_lsn ++;
/* Update chapter */
if( p_vcd->b_valid_ep &&
/* FIXME kludge so that read does not update chapter
* when a manual chapter change was requested and not
* yet accomplished */
!p_input->stream.p_new_area )
{
unsigned int i_entry = p_input->stream.p_selected_area->i_part;
vlc_mutex_lock( &p_input->stream.stream_lock );
if( i_entry < p_vcd->num_entries &&
p_vcd->cur_lsn >= p_vcd->p_entries[i_entry+1] )
{
dbg_print( INPUT_DBG_PBC,
"new entry, i_entry %d, sector %d, es %d",
i_entry, p_vcd->cur_lsn,
p_vcd->p_entries[i_entry] );
p_vcd->play_item.num =
++ p_input->stream.p_selected_area->i_part;
p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
VCDUpdateVar( p_input, p_vcd->play_item.num, VLC_VAR_SETVALUE,
"chapter", "Setting entry" );
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
i_read += M2F2_SECTOR_SIZE;
}
if ( i_len % M2F2_SECTOR_SIZE ) /* this should not happen */
{
if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
p_vcd->cur_lsn, p_last_sector ) < 0 )
{
LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
return -1;
}
p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * M2F2_SECTOR_SIZE,
p_last_sector, i_len % M2F2_SECTOR_SIZE );
i_read += i_len % M2F2_SECTOR_SIZE;
}
return i_read;
}
/*****************************************************************************
* VCDSetProgram: Does nothing since a VCD is mono_program
*****************************************************************************/
static int
VCDSetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_program)
{
thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDSetProgram" );
return 0;
}
/*****************************************************************************
* VCDSetArea: initialize internal data structures and input stream data
so set subsequent reading and seeking to reflect that we are
at track x, entry or segment y.
This is called for each user navigation request, e.g. the GUI
Chapter/Title selections or in initial MRL parsing.
****************************************************************************/
int
VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
{
thread_vcd_data_t *p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
unsigned int i_entry = p_area->i_part;
track_t i_track = p_area->i_id;
int old_seekable = p_input->stream.b_seekable;
unsigned int i_nb = p_area->i_plugin_data + p_area->i_part_nb;
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT),
"track: %d, entry %d, seekable %d",
i_track, i_entry, old_seekable );
/* we can't use the interface slider until initilization is complete */
p_input->stream.b_seekable = 0;
if( p_area != p_input->stream.p_selected_area )
{
unsigned int i;
/* If is the result of a track change, make the entry valid. */
if (i_entry < p_area->i_plugin_data || i_entry >= i_nb)
i_entry = p_area->i_plugin_data;
/* Change the default area */
p_input->stream.p_selected_area = p_area;
/* Update the navigation variables without triggering a callback */
VCDUpdateVar( p_input, i_track, VLC_VAR_SETVALUE, "title",
"Setting track");
var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
for( i = p_area->i_plugin_data; i < i_nb; i++ )
{
VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
"chapter", "Adding entry choice");
}
}
if (i_track == 0)
VCDSetOrigin( p_input, p_vcd->p_segments[i_entry],
p_vcd->p_segments[i_entry], p_vcd->p_segments[i_entry+1],
i_entry, 0 );
else
VCDSetOrigin( p_input, p_vcd->p_sectors[i_track],
vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry),
p_vcd->p_sectors[i_track+1],
i_entry, i_track );
p_input->stream.b_seekable = old_seekable;
/* warn interface that something has changed */
p_input->stream.b_changed = 1;
return VLC_SUCCESS;
}
/****************************************************************************
* VCDSeek
****************************************************************************/
void
VCDSeek( input_thread_t * p_input, off_t i_off )
{
thread_vcd_data_t * p_vcd;
unsigned int i_entry=0; /* invalid entry */
p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
p_vcd->cur_lsn = p_vcd->origin_lsn + (i_off / (off_t)M2F2_SECTOR_SIZE);
vlc_mutex_lock( &p_input->stream.stream_lock );
#define p_area p_input->stream.p_selected_area
/* Find entry */
if( p_vcd->b_valid_ep )
{
for( i_entry = 1 ; i_entry < p_vcd->num_entries ; i_entry ++ )
{
if( p_vcd->cur_lsn < p_vcd->p_entries[i_entry] )
{
VCDUpdateVar( p_input, i_entry, VLC_VAR_SETVALUE,
"chapter", "Setting entry" );
break;
}
}
p_vcd->play_item.num = p_area->i_part = i_entry;
p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
}
#undef p_area
p_input->stream.p_selected_area->i_tell = i_off;
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
"orig %d, cur: %d, offset: %lld, start: %lld, entry %d",
p_vcd->origin_lsn, p_vcd->cur_lsn, i_off,
p_input->stream.p_selected_area->i_start, i_entry );
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
/*****************************************************************************
VCDPlay: set up internal structures so seeking/reading places an item.
itemid: the thing to play.
user_entry: true if itemid is a user selection (rather than internally-
generated selection such as via PBC) in which case we may have to adjust
for differences in numbering.
*****************************************************************************/
int
VCDPlay( input_thread_t *p_input, vcdinfo_itemid_t itemid )
{
thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
input_area_t * p_area;
p_vcd->in_still = 0;
#define area p_input->stream.pp_areas
switch (itemid.type) {
case VCDINFO_ITEM_TYPE_TRACK:
/* Valid tracks go from 1...num_tracks-1, because track 0 is unplayable.
*/
if (itemid.num == 0 || itemid.num >= p_vcd->num_tracks) {
LOG_ERR ("Invalid track number %d", itemid.num );
return VLC_EGENERIC;
}
p_area = area[itemid.num];
p_area->i_part = p_area->i_plugin_data;
p_input->stream.b_seekable = 1;
break;
case VCDINFO_ITEM_TYPE_SEGMENT:
/* Valid segments go from 0...num_segments-1. */
if (itemid.num >= p_vcd->num_segments) {
LOG_ERR ( "Invalid segment number: %d", itemid.num );
return VLC_EGENERIC;
} else {
vcdinfo_video_segment_type_t segtype =
vcdinfo_get_video_type(p_vcd->vcd, itemid.num);
dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d",
vcdinfo_video_type2str(p_vcd->vcd, itemid.num),
(int) segtype, itemid.num);
p_area = area[0];
p_area->i_part = itemid.num;
switch (segtype)
{
case VCDINFO_FILES_VIDEO_NTSC_STILL:
case VCDINFO_FILES_VIDEO_NTSC_STILL2:
case VCDINFO_FILES_VIDEO_PAL_STILL:
case VCDINFO_FILES_VIDEO_PAL_STILL2:
p_input->stream.b_seekable = 0;
p_vcd->in_still = -5;
break;
default:
p_input->stream.b_seekable = 1;
p_vcd->in_still = 0;
}
}
break;
case VCDINFO_ITEM_TYPE_LID:
/* LIDs go from 1..num_lids. */
if (itemid.num == 0 || itemid.num > p_vcd->num_lids) {
LOG_ERR ( "Invalid LID number: %d", itemid.num );
return VLC_EGENERIC;
} else {
p_vcd->cur_lid = itemid.num;
vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
switch (p_vcd->pxd.descriptor_type) {
case PSD_TYPE_SELECTION_LIST:
case PSD_TYPE_EXT_SELECTION_LIST: {
vcdinfo_itemid_t trans_itemid;
uint16_t trans_itemid_num;
if (p_vcd->pxd.psd == NULL) return VLC_EGENERIC;
trans_itemid_num = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
p_vcd->loop_count = 1;
p_vcd->loop_item = trans_itemid;
return VCDPlay( p_input, trans_itemid );
break;
}
case PSD_TYPE_PLAY_LIST: {
if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
p_vcd->pdi = -1;
return vcdplayer_inc_play_item(p_input)
? VLC_SUCCESS : VLC_EGENERIC;
break;
}
case PSD_TYPE_END_LIST:
case PSD_TYPE_COMMAND_LIST:
default:
;
}
}
return VLC_EGENERIC;
case VCDINFO_ITEM_TYPE_ENTRY:
/* Entries go from 0..num_entries-1. */
if (itemid.num >= p_vcd->num_entries) {
LOG_ERR ("Invalid entry number: %d", itemid.num );
return VLC_EGENERIC;
} else {
track_t cur_track = vcdinfo_get_track(p_vcd->vcd, itemid.num);
p_area = area[cur_track];
p_area->i_part = itemid.num;
p_input->stream.b_seekable = 1;
}
break;
default:
LOG_ERR ("unknown entry type" );
return VLC_EGENERIC;
}
VCDSetArea( p_input, p_area );
#undef area
p_vcd->play_item = itemid;
dbg_print( (INPUT_DBG_CALL),
"i_start %lld, i_size: %lld, i_tell: %lld, lsn %d",
p_area->i_start, p_area->i_size,
p_area->i_tell, p_vcd->cur_lsn );
return VLC_SUCCESS;
}
/*****************************************************************************
VCDEntryPoints: Reads the information about the entry points on the disc
and initializes area information with that.
Before calling this track information should have been read in.
*****************************************************************************/
static int
VCDEntryPoints( input_thread_t * p_input )
{
thread_vcd_data_t * p_vcd;
unsigned int i_nb;
unsigned int i, i_entry_index = 0;
unsigned int i_previous_track = CDIO_INVALID_TRACK;
p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
if (0 == i_nb)
return -1;
p_vcd->p_entries = malloc( sizeof( lba_t ) * i_nb );
if( p_vcd->p_entries == NULL )
{
LOG_ERR ("not enough memory for entry points treatment" );
return -1;
}
p_vcd->num_entries = 0;
for( i = 0 ; i < i_nb ; i++ )
{
track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
if( i_track <= p_input->stream.i_area_nb )
{
p_vcd->p_entries[i] =
vcdinfo_get_entry_lsn(p_vcd->vcd, i);
p_input->stream.pp_areas[i_track]->i_part_nb ++;
/* if this entry belongs to a new track */
if( i_track != i_previous_track )
{
/* i_plugin_data is used to store the first entry of the area*/
p_input->stream.pp_areas[i_track]->i_plugin_data =
i_entry_index;
i_previous_track = i_track;
p_input->stream.pp_areas[i_track]->i_part_nb = 1;
}
i_entry_index ++;
p_vcd->num_entries ++;
}
else
msg_Warn( p_input, "wrong track number found in entry points" );
}
p_vcd->b_valid_ep = true;
return 0;
}
/*****************************************************************************
* VCDSegments: Reads the information about the segments the disc.
*****************************************************************************/
static int
VCDSegments( input_thread_t * p_input )
{
thread_vcd_data_t * p_vcd;
unsigned int i;
unsigned int num_segments;
p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
num_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
#define area p_input->stream.pp_areas
/* area 0 is reserved for segments. Set Absolute start offset
and size */
area[0]->i_plugin_data = 0;
input_DelArea( p_input, area[0] );
input_AddArea( p_input, 0, 0 );
area[0]->i_start = (off_t)p_vcd->p_sectors[0]
* (off_t)M2F2_SECTOR_SIZE;
area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
* (off_t)M2F2_SECTOR_SIZE;
/* Default Segment */
area[0]->i_part = 0;
/* i_plugin_data is used to store which entry point is the first
of the track (area) */
area[0]->i_plugin_data = 0;
area[0]->i_part_nb = 0;
dbg_print( INPUT_DBG_MRL, "area id %d, for segment track %d",
area[0]->i_id, 0 );
if (num_segments == 0) return 0;
/* We have one additional segment allocated so we can get the size
by subtracting seg[i+1] - seg[i].
*/
p_vcd->p_segments = malloc( sizeof( lba_t ) * (num_segments+1) );
if( p_vcd->p_segments == NULL )
{
LOG_ERR ("not enough memory for segment treatment" );
return -1;
}
/* Update the navigation variables without triggering a callback */
VCDUpdateVar( p_input, 0, VLC_VAR_SETVALUE, "title", "Setting track" );
var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
for( i = 0 ; i < num_segments ; i++ )
{
p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
area[0]->i_part_nb ++;
VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
"chapter", "Adding segment choice");
}
#undef area
p_vcd->p_segments[num_segments] = p_vcd->p_segments[num_segments-1]+
vcdinfo_get_seg_sector_count(p_vcd->vcd, num_segments-1);
return 0;
}
/*****************************************************************************
VCDTracks: initializes area information.
Before calling this track information should have been read in.
*****************************************************************************/
static void
VCDTracks( input_thread_t * p_input )
{
thread_vcd_data_t * p_vcd;
unsigned int i;
p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
#define area p_input->stream.pp_areas
/* We start area addressing for tracks at 1 since the default area 0
is reserved for segments */
for( i = 1 ; i < p_vcd->num_tracks ; i++ )
{
/* Tracks are Program Chains */
input_AddArea( p_input, i, i );
/* Absolute start byte offset and byte size */
area[i]->i_start = (off_t) p_vcd->p_sectors[i]
* (off_t)M2F2_SECTOR_SIZE;
area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
* (off_t)M2F2_SECTOR_SIZE;
/* Current entry being played in track */
area[i]->i_part = 0;
/* i_plugin_data is used to store which entry point is the first
* of the track (area) */
area[i]->i_plugin_data = 0;
dbg_print( INPUT_DBG_MRL,
"area[%d] id: %d, i_start: %lld, i_size: %lld",
i, area[i]->i_id, area[i]->i_start, area[i]->i_size );
}
#undef area
return ;
}
/*****************************************************************************
VCDLIDs: Reads the LIST IDs from the LOT.
*****************************************************************************/
static int
VCDLIDs( input_thread_t * p_input )
{
thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
p_vcd->num_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
p_vcd->cur_lid = VCDINFO_INVALID_ENTRY;
if (vcdinfo_read_psd (p_vcd->vcd)) {
vcdinfo_visit_lot (p_vcd->vcd, false);
if (vcdinfo_get_psd_x_size(p_vcd->vcd))
vcdinfo_visit_lot (p_vcd->vcd, true);
}
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
"num LIDs=%d", p_vcd->num_lids);
return 0;
}
/*****************************************************************************
* VCDParse: parse command line
*****************************************************************************/
static char *
VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid )
{
thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
char * psz_parser;
char * psz_source;
char * psz_next;
p_itemid->type=VCDINFO_ITEM_TYPE_TRACK;
p_itemid->num=1;
#ifdef WIN32
/* On Win32 we want the VCD access plugin to be explicitly requested,
* we end up with lots of problems otherwise */
if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
#endif
if( !p_input->psz_name )
{
return NULL;
}
psz_parser = psz_source = strdup( p_input->psz_name );
/* Parse input string :
* [device][@[type][title]] */
while( *psz_parser && *psz_parser != '@' )
{
psz_parser++;
}
if( *psz_parser == '@' )
{
/* Found the divide between the source name and the
type+entry number. */
unsigned int num;
*psz_parser = '\0';
++psz_parser;
if( *psz_parser )
{
switch(*psz_parser) {
case 'E':
p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
++psz_parser;
break;
case 'P':
p_itemid->type = VCDINFO_ITEM_TYPE_LID;
++psz_parser;
break;
case 'S':
p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
++psz_parser;
break;
case 'T':
p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
++psz_parser;
break;
default: ;
}
}
num = strtol( psz_parser, &psz_next, 10 );
if ( *psz_parser != '\0' && *psz_next == '\0')
{
p_itemid->num = num;
}
}
if( !*psz_source )
{
if( !p_input->psz_access )
{
return NULL;
}
psz_source = config_GetPsz( p_input, "vcd" );
if( !psz_source ) return NULL;
}
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
"source=%s entry=%d type=%d",
psz_source, p_itemid->num, p_itemid->type);
return psz_source;
}
/*
Set's start origin subsequent seeks/reads
*/
static void
VCDSetOrigin( input_thread_t *p_input, lsn_t origin_lsn,
lsn_t cur_lsn, lsn_t end_lsn, int cur_entry, track_t cur_track )
{
thread_vcd_data_t * p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
p_vcd->origin_lsn = origin_lsn;
p_vcd->cur_lsn = cur_lsn;
p_vcd->end_lsn = end_lsn;
p_vcd->cur_track = cur_track;
p_vcd->play_item.num = cur_entry;
p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
"origin: %d, cur_lsn: %d, end_lsn: %d, entry: %d, track: %d",
origin_lsn, cur_lsn, end_lsn, cur_entry, cur_track );
p_input->stream.p_selected_area->i_tell =
(off_t) (p_vcd->cur_lsn - p_vcd->origin_lsn) * (off_t)M2F2_SECTOR_SIZE;
VCDUpdateVar( p_input, cur_entry, VLC_VAR_SETVALUE,
"chapter", "Setting entry");
}
/*****************************************************************************
* vcd_Open: Opens a VCD device or file and returns an opaque handle
*****************************************************************************/
static vcdinfo_obj_t *
vcd_Open( vlc_object_t *p_this, const char *psz_dev )
{
vcdinfo_obj_t *p_vcdobj;
char *actual_dev;
if( !psz_dev ) return NULL;
/* Set where to log errors messages from libcdio. */
p_vcd_input = (input_thread_t *)p_this;
cdio_log_set_handler ( cdio_log_handler );
vcd_log_set_handler ( vcd_log_handler );
actual_dev=strdup(psz_dev);
if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) !=
VCDINFO_OPEN_VCD) {
free(actual_dev);
return NULL;
}
free(actual_dev);
return p_vcdobj;
}
/****************************************************************************
* VCDReadSector: Read a sector (2324 bytes)
****************************************************************************/
static int
VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
lsn_t cur_lsn, byte_t * p_buffer )
{
typedef struct {
uint8_t subheader [8];
uint8_t data [M2F2_SECTOR_SIZE];
} vcdsector_t;
vcdsector_t vcd_sector;
if (cdio_read_mode2_sector(vcdinfo_get_cd_image(p_vcd),
&vcd_sector, cur_lsn, true)
!= 0)
{
msg_Warn( p_this, "Could not read LSN %d", cur_lsn );
return -1;
}
memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
return( 0 );
}
/****************************************************************************
Update the "varname" variable to i_num without triggering a callback.
****************************************************************************/
static void
VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
const char *varname, const char *label)
{
vlc_value_t val;
val.i_int = i_num;
if (NULL != p_vcd_input) {
thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
dbg_print( INPUT_DBG_PBC, "%s %d", label, i_num );
}
var_Change( p_input, varname, i_action, &val, NULL );
}
#endif
/****************************************************************************
* cdrom.c: cdrom tools
*****************************************************************************
* Copyright (C) 1998-2001 VideoLAN
* $Id: cdrom.c,v 1.1 2003/10/04 18:55:13 gbazin Exp $
*
* Authors: Johan Bilien <jobi@via.ecp.fr>
* Gildas Bazin <gbazin@netcourrier.com>
* Jon Lech Johansen <jon-vl@nanocrew.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <vlc/vlc.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <string.h>
#include <errno.h>
#include "cdrom.h"
/*****************************************************************************
* Local Prototypes
*****************************************************************************/
static void cd_log_handler (cdio_log_level_t level, const char message[]);
/*****************************************************************************
* ioctl_Open: Opens a VCD device or file and returns an opaque handle
*****************************************************************************/
cddev_t *ioctl_Open( vlc_object_t *p_this, const char *psz_dev )
{
cddev_t *p_cddev;
if( !psz_dev ) return NULL;
/*
* Initialize structure with default values
*/
p_cddev = (cddev_t *)malloc( sizeof(cddev_t) );
if( p_cddev == NULL )
{
msg_Err( p_this, "out of memory" );
return NULL;
}
/* Set where to log errors messages from libcdio. */
cdio_log_set_handler ( cd_log_handler );
p_cddev->cdio = cdio_open(psz_dev, DRIVER_UNKNOWN);
if( p_cddev->cdio == NULL )
{
free( p_cddev );
p_cddev = NULL;
}
return p_cddev;
}
/*****************************************************************************
* ioctl_Close: Closes an already opened VCD device or file.
*****************************************************************************/
void ioctl_Close( cddev_t *p_cddev )
{
cdio_destroy(p_cddev->cdio);
}
/*****************************************************************************
* ioctl_GetTracksMap: Read the Table of Contents, fill in the pp_sectors map
* if pp_sectors is not null and return the number of
* tracks available.
* We allocate and fill one more track than are on
* the CD. The last "track" is leadout track information.
* This makes finding the end of the last track uniform
* how it is done for other tracks.
*****************************************************************************/
track_t ioctl_GetTracksMap( vlc_object_t *p_this, const CdIo *cdio,
lsn_t **pp_sectors )
{
track_t i_tracks = cdio_get_num_tracks(cdio);
track_t first_track = cdio_get_first_track_num(cdio);
track_t i;
*pp_sectors = malloc( (i_tracks + 1) * sizeof(lsn_t) );
if( *pp_sectors == NULL )
{
msg_Err( p_this, "out of memory" );
return 0;
}
/* Fill the p_sectors structure with the track/sector matches.
Note cdio_get_track_lsn when given num_tracks + 1 will return
the leadout LSN.
*/
for( i = 0 ; i <= i_tracks ; i++ )
{
(*pp_sectors)[ i ] = cdio_get_track_lsn(cdio, first_track+i);
}
return i_tracks;
}
/****************************************************************************
* ioctl_ReadSector: Read a sector (2324 bytes)
****************************************************************************/
int ioctl_ReadSector( vlc_object_t *p_this, const cddev_t *p_cddev,
int i_sector, byte_t * p_buffer )
{
typedef struct {
uint8_t subheader [8];
uint8_t data [M2F2_SECTOR_SIZE];
} vcdsector_t;
vcdsector_t vcd_sector;
if (cdio_read_mode2_sector(p_cddev->cdio, &vcd_sector, i_sector, true)
!= 0)
{
// msg_Err( p_this, "Could not read sector %d", i_sector );
return -1;
}
memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
return( 0 );
}
/****************************************************************************
* Private functions
****************************************************************************/
/* For now we're going to just discard error messages from libcdio... */
static void
cd_log_handler (cdio_log_level_t level, const char message[])
{
return;
}
/****************************************************************************
* cdrom.h: cdrom tools header
*****************************************************************************
* Copyright (C) 1998-2001 VideoLAN
* $Id: cdrom.h,v 1.1 2003/10/04 18:55:13 gbazin Exp $
*
* Authors: Johan Bilien <jobi@via.ecp.fr>
* Gildas Bazin <gbazin@netcourrier.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#include <cdio/cdio.h>
#include <cdio/logging.h>
/*****************************************************************************
* The cddev structure
*****************************************************************************/
typedef struct cddev_s
{
int *p_sectors; /* tracks layout on the vcd */
CdIo *cdio; /* libcdio uses this to read */
} cddev_t;
/*****************************************************************************
* Prototypes
*****************************************************************************/
cddev_t *ioctl_Open ( vlc_object_t *, const char * );
void ioctl_Close ( cddev_t * );
track_t ioctl_GetTracksMap ( vlc_object_t *, const CdIo *, lsn_t ** );
int ioctl_ReadSector ( vlc_object_t *, const cddev_t *,
int, byte_t * );
/*****************************************************************************
* 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: Stphane Borel <stef@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/intf.h>
#include "../../demux/mpeg/system.h"
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#ifdef STRNCASECMP_IN_STRINGS_H
# include <strings.h>
#endif
#include "vcd.h"
#include "intf.h"
/* how many packets vcdx_Demux will read in each loop */
/* #define vcdplay_READ_ONCE 64 */
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Demux ( input_thread_t * );
/*****************************************************************************
* Private structure
*****************************************************************************/
struct demux_sys_t
{
vcd_data_t * p_vcd;
module_t * p_module;
mpeg_demux_t mpeg;
};
/*****************************************************************************
* InitVCD: initializes structures
*****************************************************************************/
int E_(InitVCD) ( vlc_object_t *p_this )
{
input_thread_t *p_input = (input_thread_t *)p_this;
vcd_data_t * p_vcd = (vcd_data_t *)p_input->p_access_data;
demux_sys_t * p_demux;
printf("++++ InitVCD CALLED\n");
if( p_input->stream.i_method != INPUT_METHOD_VCD )
{
return VLC_EGENERIC;
}
p_demux = p_input->p_demux_data = malloc( sizeof(demux_sys_t ) );
if( p_demux == NULL )
{
return VLC_ENOMEM;
}
p_input->p_private = (void*)&p_demux->mpeg;
p_demux->p_module = module_Need( p_input, "mpeg-system", NULL );
if( p_demux->p_module == NULL )
{
free( p_input->p_demux_data );
return VLC_ENOMOD;
}
p_input->p_demux_data->p_vcd = p_vcd;
p_input->pf_demux = Demux;
p_input->pf_rewind = NULL;
p_vcd->p_intf = NULL;
p_vcd->i_still_time = 0;
return VLC_SUCCESS;
}
/*****************************************************************************
* EndVCD: frees unused data
*****************************************************************************/
void E_(EndVCD) ( vlc_object_t *p_this )
{
input_thread_t *p_input = (input_thread_t *)p_this;
vcd_data_t * p_vcd = p_input->p_demux_data->p_vcd;
intf_thread_t * p_intf = NULL;
p_intf = vlc_object_find( p_input, VLC_OBJECT_INTF, FIND_CHILD );
if( p_intf != NULL )
{
intf_StopThread( p_intf );
vlc_object_detach( p_intf );
vlc_object_release( p_intf );
intf_Destroy( p_intf );
}
p_vcd->p_intf = NULL;
module_Unneed( p_input, p_input->p_demux_data->p_module );
free( p_input->p_demux_data );
}
/*****************************************************************************
* Demux
*****************************************************************************/
static int Demux( input_thread_t * p_input )
{
vcd_data_t * p_vcd;
data_packet_t * p_data;
ssize_t i_result;
ptrdiff_t i_remains;
int i_data_nb = 0;
p_vcd = p_input->p_demux_data->p_vcd;
/* Read headers to compute payload length */
do
{
i_result = p_input->p_demux_data->mpeg.pf_read_ps( p_input, &p_data );
if( i_result <= 0 )
{
return i_result;
}
i_remains = p_input->p_last_data - p_input->p_current_data;
p_input->p_demux_data->mpeg.pf_demux_ps( p_input, p_data );
++i_data_nb;
}
while( i_remains );
// if( p_vcd->b_still && p_vcd->b_end_of_cell && p_vcd->p_intf != NULL )
if( p_vcd->i_still_time && p_vcd->b_end_of_cell && p_vcd->p_intf != NULL )
{
pgrm_descriptor_t * p_pgrm;
/* when we receive still_time flag, we have to pause immediately */
input_SetStatus( p_input, INPUT_STATUS_PAUSE );
vcdIntfStillTime( p_vcd->p_intf, p_vcd->i_still_time );
p_vcd->i_still_time = 0;
vlc_mutex_lock( &p_input->stream.stream_lock );
p_pgrm = p_input->stream.p_selected_program;
p_pgrm->i_synchro_state = SYNCHRO_REINIT;
vlc_mutex_unlock( &p_input->stream.stream_lock );
input_ClockManageControl( p_input, p_pgrm, 0 );
}
return i_data_nb;
}
/*****************************************************************************
* 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: Stphane Borel <stef@via.ecp.fr>
* Current modification and breakage for VCD by rocky.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h> /* malloc(), free() */
#include <string.h>
#include <unistd.h>
#include <vlc/vlc.h>
#include <vlc/intf.h>
#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
#include "vcd.h"
/*****************************************************************************
* intf_sys_t: description and status of interface
*****************************************************************************/
struct intf_sys_t
{
input_thread_t * p_input;
vcd_data_t * p_vcd;
vlc_bool_t b_still;
vlc_bool_t b_inf_still;
mtime_t m_still_time;
#if FINISHED
vcdplay_ctrl_t control;
#else
int control;
#endif
vlc_bool_t b_click, b_move, b_key_pressed;
};
/*****************************************************************************
* Local prototypes.
*****************************************************************************/
static int InitThread ( intf_thread_t *p_intf );
static int MouseEvent ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static int KeyEvent ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
/* Exported functions */
static void RunIntf ( intf_thread_t *p_intf );
/*****************************************************************************
* OpenIntf: initialize dummy interface
*****************************************************************************/
int E_(VCDOpenIntf) ( vlc_object_t *p_this )
{
intf_thread_t *p_intf = (intf_thread_t *)p_this;
printf("+++++Called VCDOpenIntf\n");
/* Allocate instance and initialize some members */
p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
if( p_intf->p_sys == NULL )
{
return( 1 );
};
p_intf->pf_run = RunIntf;
p_intf->p_sys->m_still_time = 0;
p_intf->p_sys->b_inf_still = 0;
p_intf->p_sys->b_still = 0;
return( 0 );
}
/*****************************************************************************
* CloseIntf: destroy dummy interface
*****************************************************************************/
void E_(VCDCloseIntf) ( vlc_object_t *p_this )
{
intf_thread_t *p_intf = (intf_thread_t *)p_this;
/* Destroy structure */
free( p_intf->p_sys );
}
/*****************************************************************************
* RunIntf: main loop
*****************************************************************************/
static void RunIntf( intf_thread_t *p_intf )
{
vlc_object_t * p_vout = NULL;
printf("+++++Called RunIntf\n");
if( InitThread( p_intf ) < 0 )
{
msg_Err( p_intf, "can't initialize intf" );
return;
}
msg_Dbg( p_intf, "intf initialized" );
/* Main loop */
while( !p_intf->b_die )
{
vlc_mutex_lock( &p_intf->change_lock );
/*
* keyboard event
*/
if( p_vout && p_intf->p_sys->b_key_pressed )
{
p_intf->p_sys->b_key_pressed = VLC_FALSE;
printf("++++key pressed...\n");
}
vlc_mutex_unlock( &p_intf->change_lock );
if( p_vout == NULL )
{
p_vout = vlc_object_find( p_intf->p_sys->p_input,
VLC_OBJECT_VOUT, FIND_CHILD );
if( p_vout )
{
var_AddCallback( p_vout, "mouse-moved", MouseEvent, p_intf );
var_AddCallback( p_vout, "mouse-clicked", MouseEvent, p_intf );
var_AddCallback( p_vout, "key-pressed", KeyEvent, p_intf );
}
}
/* Wait a bit */
msleep( INTF_IDLE_SLEEP );
}
vlc_object_release( p_intf->p_sys->p_input );
}
/*****************************************************************************
* InitThread:
*****************************************************************************/
static int InitThread( intf_thread_t * p_intf )
{
/* We might need some locking here */
if( !p_intf->b_die )
{
input_thread_t * p_input;
vcd_data_t * p_vcd;
p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_PARENT );
/* Maybe the input just died */
if( p_input == NULL )
{
return VLC_EGENERIC;
}
p_vcd = (vcd_data_t*)p_input->p_access_data;
p_vcd->p_intf = p_intf;
vlc_mutex_lock( &p_intf->change_lock );
p_intf->p_sys->p_input = p_input;
p_intf->p_sys->p_vcd = p_vcd;
p_intf->p_sys->b_move = VLC_FALSE;
p_intf->p_sys->b_click = VLC_FALSE;
p_intf->p_sys->b_key_pressed = VLC_FALSE;
vlc_mutex_unlock( &p_intf->change_lock );
return VLC_SUCCESS;
}
else
{
return VLC_EGENERIC;
}
}
/*****************************************************************************
* MouseEvent: callback for mouse events
*****************************************************************************/
static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
intf_thread_t *p_intf = (intf_thread_t *)p_data;
vlc_mutex_lock( &p_intf->change_lock );
if( psz_var[6] == 'c' ) /* "mouse-clicked" */
{
p_intf->p_sys->b_click = VLC_TRUE;
}
else if( psz_var[6] == 'm' ) /* "mouse-moved" */
{
p_intf->p_sys->b_move = VLC_TRUE;
}
vlc_mutex_unlock( &p_intf->change_lock );
return VLC_SUCCESS;
}
/*****************************************************************************
* KeyEvent: callback for keyboard events
*****************************************************************************/
static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
intf_thread_t *p_intf = (intf_thread_t *)p_data;
vlc_mutex_lock( &p_intf->change_lock );
p_intf->p_sys->b_key_pressed = VLC_TRUE;
vlc_mutex_unlock( &p_intf->change_lock );
return VLC_SUCCESS;
}
/*****************************************************************************
* dvdIntfStillTime: function provided to demux plugin to request
* still images
*****************************************************************************/
int vcdIntfStillTime( intf_thread_t *p_intf, int i_sec )
{
vlc_mutex_lock( &p_intf->change_lock );
if( i_sec == 0xff )
{
p_intf->p_sys->b_still = 1;
p_intf->p_sys->b_inf_still = 1;
}
else if( i_sec > 0 )
{
p_intf->p_sys->b_still = 1;
p_intf->p_sys->m_still_time = 1000000 * i_sec;
}
vlc_mutex_unlock( &p_intf->change_lock );
return VLC_SUCCESS;
}
/*****************************************************************************
* vcdIntfStillTime: function provided to reset still image
*****************************************************************************/
int vcdIntfResetStillTime( intf_thread_t *p_intf )
{
vlc_mutex_lock( &p_intf->change_lock );
p_intf->p_sys->m_still_time = 0;
input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
vlc_mutex_unlock( &p_intf->change_lock );
return VLC_SUCCESS;
}
/*****************************************************************************
* intf.h: send info to intf.
*****************************************************************************
* Copyright (C) 2001 VideoLAN
* $Id: intf.h,v 1.1 2003/10/04 18:55:13 gbazin Exp $
*
* Author: Stéphane Borel <stef@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
int vcdIntfStillTime( struct intf_thread_t *, int );
int vcdIntfResetStillTime( intf_thread_t *p_intf );
/*****************************************************************************
* vcd.c : VCD input module for vlc
* using libcdio, libvcd and libvcdinfo. vlc-specific things tend
* to go here.
*****************************************************************************
* Copyright (C) 2000 VideoLAN
* $Id: vcd.c,v 1.1 2003/10/04 18:55:13 gbazin Exp $
*
* Authors: Johan Bilien <jobi@via.ecp.fr>
* Rocky Bernstein <rocky@panix.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "../../demux/mpeg/system.h"
#include "vcd.h"
#include "intf.h"
#include "vcdplayer.h"
#include <cdio/cdio.h>
#include <cdio/cd_types.h>
#include <cdio/logging.h>
#include <cdio/util.h>
#include <libvcd/info.h>
#include <libvcd/logging.h>
#include "cdrom.h"
/* how many blocks VCDRead will read in each loop */
#define VCD_BLOCKS_ONCE 20
#define VCD_DATA_ONCE (VCD_BLOCKS_ONCE * M2F2_SECTOR_SIZE)
/*****************************************************************************
* Local prototypes
*****************************************************************************/
/* First those which are accessed from outside (via pointers). */
static int VCDOpen ( vlc_object_t * );
static void VCDClose ( vlc_object_t * );
static int VCDRead ( input_thread_t *, byte_t *, size_t );
static int VCDRead ( input_thread_t *, byte_t *, size_t );
static int VCDSetProgram ( input_thread_t *, pgrm_descriptor_t * );
/* Now those which are strictly internal */
static void VCDSetOrigin ( input_thread_t *, lsn_t origin_lsn,
lsn_t cur_lsn, lsn_t end_lsn,
int cur_entry, track_t track );
static int VCDEntryPoints ( input_thread_t * );
static int VCDLIDs ( input_thread_t * );
static int VCDSegments ( input_thread_t * );
static void VCDTracks ( input_thread_t * );
static int VCDReadSector ( vlc_object_t *p_this,
const vcdinfo_obj_t *p_vcd, lsn_t cur_lsn,
byte_t * p_buffer );
static char *VCDParse ( input_thread_t *,
/*out*/ vcdinfo_itemid_t * p_itemid );
static void VCDUpdateVar( input_thread_t *p_input, int i_entry, int i_action,
const char *varname, const char *label );
static vcdinfo_obj_t *vcd_Open ( vlc_object_t *p_this, const char *psz_dev );
static int debug_callback ( vlc_object_t *p_this, const char *psz_name,
vlc_value_t oldval, vlc_value_t val,
void *p_data );
#define DEBUG_TEXT N_("set debug mask for additional debugging.")
#define DEBUG_LONGTEXT N_( \
"This integer when viewed in binary is a debugging mask\n" \
"MRL 1\n" \
"external call 2\n" \
"all calls 4\n" \
"LSN 8\n" \
"PBC (10) 16\n" \
"libcdio (20) 32\n" \
"seeks (40) 64\n" \
"still (80) 128\n" \
"vcdinfo (100) 256\n" )
/*****************************************************************************
* Exported prototypes
*****************************************************************************/
int E_(VCDOpen) ( vlc_object_t * );
void E_(VCDClose) ( vlc_object_t * );
int E_(VCDOpenIntf) ( vlc_object_t * );
void E_(VCDCloseIntf) ( vlc_object_t * );
int E_(InitVCD) ( vlc_object_t * );
void E_(EndVCD) ( vlc_object_t * );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
add_usage_hint( N_("vcdx:[device-or-file][@{P,S,T}num]") );
set_description( _("Video CD (VCD 1.0, 1.1, 2.0, SVCD, HQVCD) input") );
set_capability( "access", 85 /* slightly higher than vcd */ );
set_callbacks( E_(VCDOpen), E_(VCDClose) );
add_shortcut( "vcd" );
add_shortcut( "vcdx" );
/* Configuration options */
add_category_hint( N_("VCDX"), NULL, VLC_TRUE );
add_integer ( MODULE_STRING "-debug", 0, debug_callback, DEBUG_TEXT,
DEBUG_LONGTEXT, VLC_TRUE );
#ifdef FIXED
add_submodule();
set_capability( "demux", 0 );
set_callbacks( E_(InitVCD), E_(EndVCD) );
#endif
add_submodule();
set_capability( "interface", 0 );
set_callbacks( E_(VCDOpenIntf), E_(VCDCloseIntf) );
vlc_module_end();
/****************************************************************************
* Private functions
****************************************************************************/
/* FIXME: This variable is a hack. Would be nice to eliminate. */
static input_thread_t *p_vcd_input = NULL;
static int
debug_callback ( vlc_object_t *p_this, const char *psz_name,
vlc_value_t oldval, vlc_value_t val, void *p_data )
{
thread_vcd_data_t *p_vcd;
if (NULL == p_vcd_input) return VLC_EGENERIC;
p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d",
p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
}
p_vcd->i_debug = val.i_int;
return VLC_SUCCESS;
}
/* process messages that originate from libcdio. */
static void
cdio_log_handler (cdio_log_level_t level, const char message[])
{
thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
switch (level) {
case CDIO_LOG_DEBUG:
case CDIO_LOG_INFO:
if (p_vcd->i_debug & INPUT_DBG_CDIO)
msg_Dbg( p_vcd_input, message);
break;
case CDIO_LOG_WARN:
msg_Warn( p_vcd_input, message);
break;
case CDIO_LOG_ERROR:
case CDIO_LOG_ASSERT:
msg_Err( p_vcd_input, message);
break;
default:
msg_Warn( p_vcd_input, message,
_("The above message had unknown vcdimager log level"),
level);
}
return;
}
/* process messages that originate from vcdinfo. */
static void
vcd_log_handler (vcd_log_level_t level, const char message[])
{
thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
switch (level) {
case VCD_LOG_DEBUG:
case VCD_LOG_INFO:
if (p_vcd->i_debug & INPUT_DBG_VCDINFO)
msg_Dbg( p_vcd_input, message);
break;
case VCD_LOG_WARN:
msg_Warn( p_vcd_input, message);
break;
case VCD_LOG_ERROR:
case VCD_LOG_ASSERT:
msg_Err( p_vcd_input, message);
break;
default:
msg_Warn( p_vcd_input, "%s\n%s %d", message,
_("The above message had unknown vcdimager log level"),
level);
}
return;
}
/*
* Data reading functions
*/
/*****************************************************************************
VCDOpen: open VCD.
read in meta-information about VCD: the number of tracks, segments,
entries, size and starting information. Then set up state variables so
that we read/seek starting at the location specified.
On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
and VLC_EGENERIC for some other error.
*****************************************************************************/
static int
VCDOpen( vlc_object_t *p_this )
{
input_thread_t * p_input = (input_thread_t *)p_this;
thread_vcd_data_t * p_vcd;
char * psz_source;
vcdinfo_itemid_t itemid;
bool play_ok;
p_input->pf_read = VCDRead;
p_input->pf_seek = VCDSeek;
p_input->pf_set_area = VCDSetArea;
p_input->pf_set_program = VCDSetProgram;
p_vcd = malloc( sizeof(thread_vcd_data_t) );
if( p_vcd == NULL )
{
LOG_ERR ("out of memory" );
return VLC_ENOMEM;
}
p_input->p_access_data = (void *)p_vcd;
p_vcd->i_debug = config_GetInt( p_this, MODULE_STRING "-debug" );
psz_source = VCDParse( p_input, &itemid );
if ( NULL == psz_source )
{
free( p_vcd );
return( VLC_EGENERIC );
}
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "%s", psz_source );
p_vcd->p_segments = NULL;
p_vcd->p_entries = NULL;
/* set up input */
p_input->i_mtu = VCD_DATA_ONCE;
vlc_mutex_lock( &p_input->stream.stream_lock );
/* If we are here we can control the pace... */
p_input->stream.b_pace_control = 1;
p_input->stream.b_seekable = 1;
p_input->stream.p_selected_area->i_size = 0;
p_input->stream.p_selected_area->i_tell = 0;
vlc_mutex_unlock( &p_input->stream.stream_lock );
if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
{
msg_Warn( p_input, "could not open %s", psz_source );
free( psz_source );
free( p_vcd );
return VLC_EGENERIC;
}
/* Get track information. */
p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
vcdinfo_get_cd_image(p_vcd->vcd),
&p_vcd->p_sectors );
free( psz_source );
if( p_vcd->num_tracks < 0 )
LOG_ERR ("unable to count tracks" );
else if( p_vcd->num_tracks <= 1 )
LOG_ERR ("no movie tracks found" );
if( p_vcd->num_tracks <= 1)
{
vcdinfo_close( p_vcd->vcd );
free( p_vcd );
return VLC_EGENERIC;
}
/* Set stream and area data */
vlc_mutex_lock( &p_input->stream.stream_lock );
/* Initialize ES structures */
input_InitStream( p_input, sizeof( stream_ps_data_t ) );
/* disc input method */
p_input->stream.i_method = INPUT_METHOD_VCD;
p_input->stream.i_area_nb = 1;
/* Initialize segment information. */
VCDSegments( p_input );
/* Initialize track area information. */
VCDTracks( p_input );
if( VCDEntryPoints( p_input ) < 0 )
{
msg_Warn( p_input, "could not read entry points, will not use them" );
p_vcd->b_valid_ep = false;
}
if( VCDLIDs( p_input ) < 0 )
{
msg_Warn( p_input, "could not read entry LIDs" );
}
play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
vlc_mutex_unlock( &p_input->stream.stream_lock );
if ( ! play_ok ) {
vcdinfo_close( p_vcd->vcd );
free( p_vcd );
return VLC_EGENERIC;
}
if( !p_input->psz_demux || !*p_input->psz_demux )
{
#if FIXED
p_input->psz_demux = "vcdx";
#else
p_input->psz_demux = "ps";
#endif
}
return VLC_SUCCESS;
}
/*****************************************************************************
* VCDClose: closes VCD releasing allocated memory.
*****************************************************************************/
static void
VCDClose( vlc_object_t *p_this )
{
input_thread_t * p_input = (input_thread_t *)p_this;
thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
vcdinfo_close( p_vcd->vcd );
free( p_vcd->p_entries );
free( p_vcd->p_segments );
free( p_vcd );
p_vcd_input = NULL;
}
/*****************************************************************************
* VCDRead: reads i_len bytes from the VCD into p_buffer.
*****************************************************************************
* Returns -1 in case of error, 0 in case of EOF, otherwise the number of
* bytes.
*****************************************************************************/
static int
VCDRead( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
{
thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
int i_blocks;
int i_index;
int i_read;
byte_t p_last_sector[ M2F2_SECTOR_SIZE ];
i_read = 0;
/* Compute the number of blocks we have to read */
i_blocks = i_len / M2F2_SECTOR_SIZE;
for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
{
if ( p_vcd->cur_lsn == p_vcd->end_lsn ) {
vcdplayer_read_status_t read_status;
/* We've run off of the end of this entry. Do we continue or stop? */
dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
"end reached, cur: %u", p_vcd->cur_lsn );
read_status = vcdplayer_pbc_is_on( p_vcd )
? vcdplayer_pbc_nav( p_input )
: vcdplayer_non_pbc_nav( p_input );
switch (read_status) {
case READ_END:
/* End reached. Return NULL to indicated this. */
case READ_ERROR:
/* Some sort of error. */
return i_read;
case READ_STILL_FRAME:
{
byte_t * p_buf = p_buffer;
p_buf += (i_index*M2F2_SECTOR_SIZE);
memset(p_buf, 0, M2F2_SECTOR_SIZE);
p_buf += 2;
*p_buf = 0x01;
dbg_print(INPUT_DBG_STILL, "Handled still event\n");
return i_read + M2F2_SECTOR_SIZE;
}
default:
case READ_BLOCK:
break;
}
}
if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
p_vcd->cur_lsn,
p_buffer + (i_index*M2F2_SECTOR_SIZE) ) < 0 )
{
LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
return -1;
}
p_vcd->cur_lsn ++;
/* Update chapter */
if( p_vcd->b_valid_ep &&
/* FIXME kludge so that read does not update chapter
* when a manual chapter change was requested and not
* yet accomplished */
!p_input->stream.p_new_area )
{
unsigned int i_entry = p_input->stream.p_selected_area->i_part;
vlc_mutex_lock( &p_input->stream.stream_lock );
if( i_entry < p_vcd->num_entries &&
p_vcd->cur_lsn >= p_vcd->p_entries[i_entry+1] )
{
dbg_print( INPUT_DBG_PBC,
"new entry, i_entry %d, sector %d, es %d",
i_entry, p_vcd->cur_lsn,
p_vcd->p_entries[i_entry] );
p_vcd->play_item.num =
++ p_input->stream.p_selected_area->i_part;
p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
VCDUpdateVar( p_input, p_vcd->play_item.num, VLC_VAR_SETVALUE,
"chapter", "Setting entry" );
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
i_read += M2F2_SECTOR_SIZE;
}
if ( i_len % M2F2_SECTOR_SIZE ) /* this should not happen */
{
if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
p_vcd->cur_lsn, p_last_sector ) < 0 )
{
LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
return -1;
}
p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * M2F2_SECTOR_SIZE,
p_last_sector, i_len % M2F2_SECTOR_SIZE );
i_read += i_len % M2F2_SECTOR_SIZE;
}
return i_read;
}
/*****************************************************************************
* VCDSetProgram: Does nothing since a VCD is mono_program
*****************************************************************************/
static int
VCDSetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_program)
{
thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDSetProgram" );
return 0;
}
/*****************************************************************************
* VCDSetArea: initialize internal data structures and input stream data
so set subsequent reading and seeking to reflect that we are
at track x, entry or segment y.
This is called for each user navigation request, e.g. the GUI
Chapter/Title selections or in initial MRL parsing.
****************************************************************************/
int
VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
{
thread_vcd_data_t *p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
unsigned int i_entry = p_area->i_part;
track_t i_track = p_area->i_id;
int old_seekable = p_input->stream.b_seekable;
unsigned int i_nb = p_area->i_plugin_data + p_area->i_part_nb;
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT),
"track: %d, entry %d, seekable %d",
i_track, i_entry, old_seekable );
/* we can't use the interface slider until initilization is complete */
p_input->stream.b_seekable = 0;
if( p_area != p_input->stream.p_selected_area )
{
unsigned int i;
/* If is the result of a track change, make the entry valid. */
if (i_entry < p_area->i_plugin_data || i_entry >= i_nb)
i_entry = p_area->i_plugin_data;
/* Change the default area */
p_input->stream.p_selected_area = p_area;
/* Update the navigation variables without triggering a callback */
VCDUpdateVar( p_input, i_track, VLC_VAR_SETVALUE, "title",
"Setting track");
var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
for( i = p_area->i_plugin_data; i < i_nb; i++ )
{
VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
"chapter", "Adding entry choice");
}
}
if (i_track == 0)
VCDSetOrigin( p_input, p_vcd->p_segments[i_entry],
p_vcd->p_segments[i_entry], p_vcd->p_segments[i_entry+1],
i_entry, 0 );
else
VCDSetOrigin( p_input, p_vcd->p_sectors[i_track],
vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry),
p_vcd->p_sectors[i_track+1],
i_entry, i_track );
p_input->stream.b_seekable = old_seekable;
/* warn interface that something has changed */
p_input->stream.b_changed = 1;
return VLC_SUCCESS;
}
/****************************************************************************
* VCDSeek
****************************************************************************/
void
VCDSeek( input_thread_t * p_input, off_t i_off )
{
thread_vcd_data_t * p_vcd;
unsigned int i_entry=0; /* invalid entry */
p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
p_vcd->cur_lsn = p_vcd->origin_lsn + (i_off / (off_t)M2F2_SECTOR_SIZE);
vlc_mutex_lock( &p_input->stream.stream_lock );
#define p_area p_input->stream.p_selected_area
/* Find entry */
if( p_vcd->b_valid_ep )
{
for( i_entry = 1 ; i_entry < p_vcd->num_entries ; i_entry ++ )
{
if( p_vcd->cur_lsn < p_vcd->p_entries[i_entry] )
{
VCDUpdateVar( p_input, i_entry, VLC_VAR_SETVALUE,
"chapter", "Setting entry" );
break;
}
}
p_vcd->play_item.num = p_area->i_part = i_entry;
p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
}
#undef p_area
p_input->stream.p_selected_area->i_tell = i_off;
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
"orig %d, cur: %d, offset: %lld, start: %lld, entry %d",
p_vcd->origin_lsn, p_vcd->cur_lsn, i_off,
p_input->stream.p_selected_area->i_start, i_entry );
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
/*****************************************************************************
VCDPlay: set up internal structures so seeking/reading places an item.
itemid: the thing to play.
user_entry: true if itemid is a user selection (rather than internally-
generated selection such as via PBC) in which case we may have to adjust
for differences in numbering.
*****************************************************************************/
int
VCDPlay( input_thread_t *p_input, vcdinfo_itemid_t itemid )
{
thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
input_area_t * p_area;
p_vcd->in_still = 0;
#define area p_input->stream.pp_areas
switch (itemid.type) {
case VCDINFO_ITEM_TYPE_TRACK:
/* Valid tracks go from 1...num_tracks-1, because track 0 is unplayable.
*/
if (itemid.num == 0 || itemid.num >= p_vcd->num_tracks) {
LOG_ERR ("Invalid track number %d", itemid.num );
return VLC_EGENERIC;
}
p_area = area[itemid.num];
p_area->i_part = p_area->i_plugin_data;
p_input->stream.b_seekable = 1;
break;
case VCDINFO_ITEM_TYPE_SEGMENT:
/* Valid segments go from 0...num_segments-1. */
if (itemid.num >= p_vcd->num_segments) {
LOG_ERR ( "Invalid segment number: %d", itemid.num );
return VLC_EGENERIC;
} else {
vcdinfo_video_segment_type_t segtype =
vcdinfo_get_video_type(p_vcd->vcd, itemid.num);
dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d",
vcdinfo_video_type2str(p_vcd->vcd, itemid.num),
(int) segtype, itemid.num);
p_area = area[0];
p_area->i_part = itemid.num;
switch (segtype)
{
case VCDINFO_FILES_VIDEO_NTSC_STILL:
case VCDINFO_FILES_VIDEO_NTSC_STILL2:
case VCDINFO_FILES_VIDEO_PAL_STILL:
case VCDINFO_FILES_VIDEO_PAL_STILL2:
p_input->stream.b_seekable = 0;
p_vcd->in_still = -5;
break;
default:
p_input->stream.b_seekable = 1;
p_vcd->in_still = 0;
}
}
break;
case VCDINFO_ITEM_TYPE_LID:
/* LIDs go from 1..num_lids. */
if (itemid.num == 0 || itemid.num > p_vcd->num_lids) {
LOG_ERR ( "Invalid LID number: %d", itemid.num );
return VLC_EGENERIC;
} else {
p_vcd->cur_lid = itemid.num;
vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
switch (p_vcd->pxd.descriptor_type) {
case PSD_TYPE_SELECTION_LIST:
case PSD_TYPE_EXT_SELECTION_LIST: {
vcdinfo_itemid_t trans_itemid;
uint16_t trans_itemid_num;
if (p_vcd->pxd.psd == NULL) return VLC_EGENERIC;
trans_itemid_num = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
p_vcd->loop_count = 1;
p_vcd->loop_item = trans_itemid;
return VCDPlay( p_input, trans_itemid );
break;
}
case PSD_TYPE_PLAY_LIST: {
if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
p_vcd->pdi = -1;
return vcdplayer_inc_play_item(p_input)
? VLC_SUCCESS : VLC_EGENERIC;
break;
}
case PSD_TYPE_END_LIST:
case PSD_TYPE_COMMAND_LIST:
default:
;
}
}
return VLC_EGENERIC;
case VCDINFO_ITEM_TYPE_ENTRY:
/* Entries go from 0..num_entries-1. */
if (itemid.num >= p_vcd->num_entries) {
LOG_ERR ("Invalid entry number: %d", itemid.num );
return VLC_EGENERIC;
} else {
track_t cur_track = vcdinfo_get_track(p_vcd->vcd, itemid.num);
p_area = area[cur_track];
p_area->i_part = itemid.num;
p_input->stream.b_seekable = 1;
}
break;
default:
LOG_ERR ("unknown entry type" );
return VLC_EGENERIC;
}
VCDSetArea( p_input, p_area );
#undef area
p_vcd->play_item = itemid;
dbg_print( (INPUT_DBG_CALL),
"i_start %lld, i_size: %lld, i_tell: %lld, lsn %d",
p_area->i_start, p_area->i_size,
p_area->i_tell, p_vcd->cur_lsn );
return VLC_SUCCESS;
}
/*****************************************************************************
VCDEntryPoints: Reads the information about the entry points on the disc
and initializes area information with that.
Before calling this track information should have been read in.
*****************************************************************************/
static int
VCDEntryPoints( input_thread_t * p_input )
{
thread_vcd_data_t * p_vcd;
unsigned int i_nb;
unsigned int i, i_entry_index = 0;
unsigned int i_previous_track = CDIO_INVALID_TRACK;
p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
if (0 == i_nb)
return -1;
p_vcd->p_entries = malloc( sizeof( lba_t ) * i_nb );
if( p_vcd->p_entries == NULL )
{
LOG_ERR ("not enough memory for entry points treatment" );
return -1;
}
p_vcd->num_entries = 0;
for( i = 0 ; i < i_nb ; i++ )
{
track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
if( i_track <= p_input->stream.i_area_nb )
{
p_vcd->p_entries[i] =
vcdinfo_get_entry_lsn(p_vcd->vcd, i);
p_input->stream.pp_areas[i_track]->i_part_nb ++;
/* if this entry belongs to a new track */
if( i_track != i_previous_track )
{
/* i_plugin_data is used to store the first entry of the area*/
p_input->stream.pp_areas[i_track]->i_plugin_data =
i_entry_index;
i_previous_track = i_track;
p_input->stream.pp_areas[i_track]->i_part_nb = 1;
}
i_entry_index ++;
p_vcd->num_entries ++;
}
else
msg_Warn( p_input, "wrong track number found in entry points" );
}
p_vcd->b_valid_ep = true;
return 0;
}
/*****************************************************************************
* VCDSegments: Reads the information about the segments the disc.
*****************************************************************************/
static int
VCDSegments( input_thread_t * p_input )
{
thread_vcd_data_t * p_vcd;
unsigned int i;
unsigned int num_segments;
p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
num_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
#define area p_input->stream.pp_areas
/* area 0 is reserved for segments. Set Absolute start offset
and size */
area[0]->i_plugin_data = 0;
input_DelArea( p_input, area[0] );
input_AddArea( p_input, 0, 0 );
area[0]->i_start = (off_t)p_vcd->p_sectors[0]
* (off_t)M2F2_SECTOR_SIZE;
area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
* (off_t)M2F2_SECTOR_SIZE;
/* Default Segment */
area[0]->i_part = 0;
/* i_plugin_data is used to store which entry point is the first
of the track (area) */
area[0]->i_plugin_data = 0;
area[0]->i_part_nb = 0;
dbg_print( INPUT_DBG_MRL, "area id %d, for segment track %d",
area[0]->i_id, 0 );
if (num_segments == 0) return 0;
/* We have one additional segment allocated so we can get the size
by subtracting seg[i+1] - seg[i].
*/
p_vcd->p_segments = malloc( sizeof( lba_t ) * (num_segments+1) );
if( p_vcd->p_segments == NULL )
{
LOG_ERR ("not enough memory for segment treatment" );
return -1;
}
/* Update the navigation variables without triggering a callback */
VCDUpdateVar( p_input, 0, VLC_VAR_SETVALUE, "title", "Setting track" );
var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
for( i = 0 ; i < num_segments ; i++ )
{
p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
area[0]->i_part_nb ++;
VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
"chapter", "Adding segment choice");
}
#undef area
p_vcd->p_segments[num_segments] = p_vcd->p_segments[num_segments-1]+
vcdinfo_get_seg_sector_count(p_vcd->vcd, num_segments-1);
return 0;
}
/*****************************************************************************
VCDTracks: initializes area information.
Before calling this track information should have been read in.
*****************************************************************************/
static void
VCDTracks( input_thread_t * p_input )
{
thread_vcd_data_t * p_vcd;
unsigned int i;
p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
#define area p_input->stream.pp_areas
/* We start area addressing for tracks at 1 since the default area 0
is reserved for segments */
for( i = 1 ; i < p_vcd->num_tracks ; i++ )
{
/* Tracks are Program Chains */
input_AddArea( p_input, i, i );
/* Absolute start byte offset and byte size */
area[i]->i_start = (off_t) p_vcd->p_sectors[i]
* (off_t)M2F2_SECTOR_SIZE;
area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
* (off_t)M2F2_SECTOR_SIZE;
/* Current entry being played in track */
area[i]->i_part = 0;
/* i_plugin_data is used to store which entry point is the first
* of the track (area) */
area[i]->i_plugin_data = 0;
dbg_print( INPUT_DBG_MRL,
"area[%d] id: %d, i_start: %lld, i_size: %lld",
i, area[i]->i_id, area[i]->i_start, area[i]->i_size );
}
#undef area
return ;
}
/*****************************************************************************
VCDLIDs: Reads the LIST IDs from the LOT.
*****************************************************************************/
static int
VCDLIDs( input_thread_t * p_input )
{
thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
p_vcd->num_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
p_vcd->cur_lid = VCDINFO_INVALID_ENTRY;
if (vcdinfo_read_psd (p_vcd->vcd)) {
vcdinfo_visit_lot (p_vcd->vcd, false);
if (vcdinfo_get_psd_x_size(p_vcd->vcd))
vcdinfo_visit_lot (p_vcd->vcd, true);
}
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
"num LIDs=%d", p_vcd->num_lids);
return 0;
}
/*****************************************************************************
* VCDParse: parse command line
*****************************************************************************/
static char *
VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid )
{
thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
char * psz_parser;
char * psz_source;
char * psz_next;
p_itemid->type=VCDINFO_ITEM_TYPE_TRACK;
p_itemid->num=1;
#ifdef WIN32
/* On Win32 we want the VCD access plugin to be explicitly requested,
* we end up with lots of problems otherwise */
if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
#endif
if( !p_input->psz_name )
{
return NULL;
}
psz_parser = psz_source = strdup( p_input->psz_name );
/* Parse input string :
* [device][@[type][title]] */
while( *psz_parser && *psz_parser != '@' )
{
psz_parser++;
}
if( *psz_parser == '@' )
{
/* Found the divide between the source name and the
type+entry number. */
unsigned int num;
*psz_parser = '\0';
++psz_parser;
if( *psz_parser )
{
switch(*psz_parser) {
case 'E':
p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
++psz_parser;
break;
case 'P':
p_itemid->type = VCDINFO_ITEM_TYPE_LID;
++psz_parser;
break;
case 'S':
p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
++psz_parser;
break;
case 'T':
p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
++psz_parser;
break;
default: ;
}
}
num = strtol( psz_parser, &psz_next, 10 );
if ( *psz_parser != '\0' && *psz_next == '\0')
{
p_itemid->num = num;
}
}
if( !*psz_source ) {
/* No source specified, so figure it out. */
if( !p_input->psz_access ) return NULL;
psz_source = config_GetPsz( p_input, MODULE_STRING "-device" );
if( !psz_source ) {
/* Scan for a CD with a VCD in it. */
char **cd_drives = cdio_get_devices_with_cap(NULL,
(CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD
|CDIO_FS_ANAL_VIDEOCD|CDIO_FS_UNKNOWN),
true);
if (NULL == cd_drives) return NULL;
if (cd_drives[0] == NULL) {
cdio_free_device_list(cd_drives);
return NULL;
}
psz_source = strdup(cd_drives[0]);
cdio_free_device_list(cd_drives);
}
}
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
"source=%s entry=%d type=%d",
psz_source, p_itemid->num, p_itemid->type);
return psz_source;
}
/*
Set's start origin subsequent seeks/reads
*/
static void
VCDSetOrigin( input_thread_t *p_input, lsn_t origin_lsn,
lsn_t cur_lsn, lsn_t end_lsn, int cur_entry, track_t cur_track )
{
thread_vcd_data_t * p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
p_vcd->origin_lsn = origin_lsn;
p_vcd->cur_lsn = cur_lsn;
p_vcd->end_lsn = end_lsn;
p_vcd->cur_track = cur_track;
p_vcd->play_item.num = cur_entry;
p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
"origin: %d, cur_lsn: %d, end_lsn: %d, entry: %d, track: %d",
origin_lsn, cur_lsn, end_lsn, cur_entry, cur_track );
p_input->stream.p_selected_area->i_tell =
(off_t) (p_vcd->cur_lsn - p_vcd->origin_lsn) * (off_t)M2F2_SECTOR_SIZE;
VCDUpdateVar( p_input, cur_entry, VLC_VAR_SETVALUE,
"chapter", "Setting entry");
}
/*****************************************************************************
* vcd_Open: Opens a VCD device or file and returns an opaque handle
*****************************************************************************/
static vcdinfo_obj_t *
vcd_Open( vlc_object_t *p_this, const char *psz_dev )
{
vcdinfo_obj_t *p_vcdobj;
char *actual_dev;
if( !psz_dev ) return NULL;
/* Set where to log errors messages from libcdio. */
p_vcd_input = (input_thread_t *)p_this;
cdio_log_set_handler ( cdio_log_handler );
vcd_log_set_handler ( vcd_log_handler );
actual_dev=strdup(psz_dev);
if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) !=
VCDINFO_OPEN_VCD) {
free(actual_dev);
return NULL;
}
free(actual_dev);
return p_vcdobj;
}
/****************************************************************************
* VCDReadSector: Read a sector (2324 bytes)
****************************************************************************/
static int
VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
lsn_t cur_lsn, byte_t * p_buffer )
{
typedef struct {
uint8_t subheader [8];
uint8_t data [M2F2_SECTOR_SIZE];
} vcdsector_t;
vcdsector_t vcd_sector;
if (cdio_read_mode2_sector(vcdinfo_get_cd_image(p_vcd),
&vcd_sector, cur_lsn, true)
!= 0)
{
msg_Warn( p_this, "Could not read LSN %d", cur_lsn );
return -1;
}
memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
return( 0 );
}
/****************************************************************************
Update the "varname" variable to i_num without triggering a callback.
****************************************************************************/
static void
VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
const char *varname, const char *label)
{
vlc_value_t val;
val.i_int = i_num;
if (NULL != p_vcd_input) {
thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
dbg_print( INPUT_DBG_PBC, "%s %d", label, i_num );
}
var_Change( p_input, varname, i_action, &val, NULL );
}
/*****************************************************************************
* vcd.h : VCD input module for vlc
* using libcdio, libvcd and libvcdinfo
*****************************************************************************
* Copyright (C) 2003 VideoLAN
* $Id: vcd.h,v 1.1 2003/10/04 18:55:13 gbazin Exp $
*
* Authors: Rocky Bernstein <rocky@panix.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#include <libvcd/info.h>
/*****************************************************************************
* dvd_data_t: structure for communication between dvdplay access, demux
* and intf.
*****************************************************************************/
/*****************************************************************************
* vcd_data_t: structure for communication between access and intf.
*****************************************************************************/
typedef struct
{
#if FINISHED
vcdplay_ptr vmg;
#endif
intf_thread_t * p_intf;
#if DEMUX_FINISHED
int i_audio_nb;
int i_spu_nb;
#endif
int i_still_time;
vlc_bool_t b_end_of_cell;
#if FINISHED
vcdplay_event_t event;
vcdplay_ctrl_t control;
vcdplay_highlight_t hli;
#endif
} vcd_data_t;
int VCDSetArea ( input_thread_t *, input_area_t * );
void VCDSeek ( input_thread_t *, off_t );
int VCDPlay ( input_thread_t *, vcdinfo_itemid_t );
/*****************************************************************************
* vcdplayer.c : VCD input module for vlc
* using libcdio, libvcd and libvcdinfo
*****************************************************************************
* Copyright (C) 2003 Rocky Bernstein <rocky@panix.com>
* $Id: vcdplayer.c,v 1.1 2003/10/04 18:55:13 gbazin Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*
This contains more of the vlc-independent parts that might be used
in any VCD input module for a media player. However at present there
are vlc-specific structures. See also vcdplayer.c of the xine plugin.
*/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "vcd.h"
#include "vcdplayer.h"
#include <string.h>
#include <cdio/cdio.h>
#include <cdio/util.h>
#include <libvcd/info.h>
/*!
Return true if playback control (PBC) is on
*/
bool
vcdplayer_pbc_is_on(const thread_vcd_data_t *p_vcd)
{
return VCDINFO_INVALID_ENTRY != p_vcd->cur_lid;
}
lid_t
vcdplayer_selection2lid ( input_thread_t *p_input, int entry_num )
{
/* FIXME: Some of this probably gets moved to vcdinfo. */
/* Convert selection number to lid and then entry number...*/
thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
unsigned int offset;
unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
vcdinfo_obj_t *obj = p_vcd->vcd;
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
"Called lid %u, entry_num %d bsn %d", p_vcd->cur_lid,
entry_num, bsn);
if ( (entry_num - bsn + 1) > 0) {
offset = vcdinfo_lid_get_offset(obj, p_vcd->cur_lid, entry_num-bsn+1);
} else {
LOG_ERR( "Selection number %u too small. bsn %u", entry_num, bsn );
return VCDINFO_INVALID_LID;
}
if (offset != VCDINFO_INVALID_OFFSET) {
vcdinfo_offset_t *ofs;
int old = entry_num;
switch (offset) {
case PSD_OFS_DISABLED:
LOG_ERR( "Selection %u disabled", entry_num );
return VCDINFO_INVALID_LID;
case PSD_OFS_MULTI_DEF:
LOG_ERR( "Selection %u multi_def", entry_num );
return VCDINFO_INVALID_LID;
case PSD_OFS_MULTI_DEF_NO_NUM:
LOG_ERR( "Selection %u multi_def_no_num", entry_num );
return VCDINFO_INVALID_LID;
default: ;
}
ofs = vcdinfo_get_offset_t(obj, offset);
if (NULL == ofs) {
LOG_ERR( "error in vcdinfo_get_offset" );
return -1;
}
dbg_print(INPUT_DBG_PBC,
"entry %u turned into selection lid %u",
old, ofs->lid);
return ofs->lid;
} else {
LOG_ERR( "invalid or unset entry %u", entry_num );
return VCDINFO_INVALID_LID;
}
}
static void
vcdplayer_update_entry( input_thread_t * p_input, uint16_t ofs,
uint16_t *entry, const char *label)
{
thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
if ( ofs == VCDINFO_INVALID_OFFSET ) {
*entry = VCDINFO_INVALID_ENTRY;
} else {
vcdinfo_offset_t *off_t = vcdinfo_get_offset_t(p_vcd->vcd, ofs);
if (off_t != NULL) {
*entry = off_t->lid;
dbg_print(INPUT_DBG_PBC, "%s: %d\n", label, off_t->lid);
} else
*entry = VCDINFO_INVALID_ENTRY;
}
}
/* Handles navigation when NOT in PBC reaching the end of a play item.
The navigations rules here may be sort of made up, but the intent
is to do something that's probably right or helpful.
return true if the caller should return.
*/
vcdplayer_read_status_t
vcdplayer_non_pbc_nav ( input_thread_t * p_input )
{
thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
/* Not in playback control. Do we advance automatically or stop? */
switch (p_vcd->play_item.type) {
case VCDINFO_ITEM_TYPE_TRACK:
case VCDINFO_ITEM_TYPE_ENTRY: {
input_area_t *p_area;
dbg_print( INPUT_DBG_LSN, "new track %d, lsn %d", p_vcd->cur_track,
p_vcd->p_sectors[p_vcd->cur_track+1] );
if ( p_vcd->cur_track >= p_vcd->num_tracks - 1 )
return READ_END; /* EOF */
p_vcd->play_item.num = p_vcd->cur_track++;
vlc_mutex_lock( &p_input->stream.stream_lock );
p_area = p_input->stream.pp_areas[p_vcd->cur_track];
p_area->i_part = 1;
VCDSetArea( p_input, p_area );
vlc_mutex_unlock( &p_input->stream.stream_lock );
return READ_BLOCK;
break;
}
case VCDINFO_ITEM_TYPE_SPAREID2:
dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN),
"SPAREID2" );
/* FIXME */
p_input->stream.b_seekable = 0;
if (p_vcd->in_still)
{
return READ_STILL_FRAME ;
}
return READ_END;
case VCDINFO_ITEM_TYPE_NOTFOUND:
LOG_ERR ("NOTFOUND outside PBC -- not supposed to happen");
return READ_ERROR;
case VCDINFO_ITEM_TYPE_LID:
LOG_ERR ("LID outside PBC -- not supposed to happen");
return READ_ERROR;
case VCDINFO_ITEM_TYPE_SEGMENT:
/* Hack: Just go back and do still again */
/* FIXME */
p_input->stream.b_seekable = 0;
if (p_vcd->in_still)
{
dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN),
"End of Segment - looping" );
return READ_STILL_FRAME;
}
return READ_END;
}
return READ_BLOCK;
}
/* FIXME: Will do whatever the right thing is later. */
#define SLEEP_1_SEC_AND_HANDLE_EVENTS sleep(1)
/* Handles PBC navigation when reaching the end of a play item. */
vcdplayer_read_status_t
vcdplayer_pbc_nav ( input_thread_t * p_input )
{
thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
/* We are in playback control. */
vcdinfo_itemid_t itemid;
if (0 != p_vcd->in_still && p_vcd->in_still != -5) {
SLEEP_1_SEC_AND_HANDLE_EVENTS;
if (p_vcd->in_still > 0) p_vcd->in_still--;
return READ_STILL_FRAME;
}
/* The end of an entry is really the end of the associated
sequence (or track). */
if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) &&
(p_vcd->cur_lsn < p_vcd->end_lsn) ) {
/* Set up to just continue to the next entry */
p_vcd->play_item.num++;
dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
"continuing into next entry: %u", p_vcd->play_item.num);
VCDPlay( p_input, p_vcd->play_item );
/* p_vcd->update_title(); */
return READ_BLOCK;
}
switch (p_vcd->pxd.descriptor_type) {
case PSD_TYPE_END_LIST:
return READ_END;
break;
case PSD_TYPE_PLAY_LIST: {
int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
if (vcdplayer_inc_play_item(p_input))
return READ_BLOCK;
/* Handle any wait time given. */
if (-5 == p_vcd->in_still) {
if (wait_time != 0) {
/* FIXME */
p_vcd->in_still = wait_time - 1;
SLEEP_1_SEC_AND_HANDLE_EVENTS ;
return READ_STILL_FRAME;
}
}
vcdplayer_update_entry( p_input,
vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
&itemid.num, "next" );
itemid.type = VCDINFO_ITEM_TYPE_LID;
VCDPlay( p_input, itemid );
break;
}
case PSD_TYPE_SELECTION_LIST: /* Selection List (+Ext. for SVCD) */
case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
{
int wait_time = vcdinf_get_timeout_time(p_vcd->pxd.psd);
uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
uint16_t max_loop = vcdinf_get_loop_count(p_vcd->pxd.psd);
vcdinfo_offset_t *offset_timeout_LID =
vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d",
wait_time, p_vcd->loop_count, max_loop);
/* Handle any wait time given */
if (-5 == p_vcd->in_still) {
p_vcd->in_still = wait_time - 1;
SLEEP_1_SEC_AND_HANDLE_EVENTS ;
return READ_STILL_FRAME;
}
/* Handle any looping given. */
if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
p_vcd->loop_count++;
if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
VCDSeek( p_input, 0 );
/* if (p_vcd->in_still) p_vcd->force_redisplay();*/
return READ_BLOCK;
}
/* Looping finished and wait finished. Move to timeout
entry or next entry, or handle still. */
if (NULL != offset_timeout_LID) {
/* Handle timeout_LID */
itemid.num = offset_timeout_LID->lid;
itemid.type = VCDINFO_ITEM_TYPE_LID;
dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
VCDPlay( p_input, itemid );
return READ_BLOCK;
} else {
int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
if (num_selections > 0) {
/* Pick a random selection. */
unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
int rand_selection=bsn +
(int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
lid_t rand_lid=vcdplayer_selection2lid (p_input, rand_selection);
itemid.num = rand_lid;
itemid.type = VCDINFO_ITEM_TYPE_LID;
dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d",
rand_selection - bsn, rand_lid);
VCDPlay( p_input, itemid );
return READ_BLOCK;
} else if (p_vcd->in_still) {
/* Hack: Just go back and do still again */
SLEEP_1_SEC_AND_HANDLE_EVENTS ;
return READ_STILL_FRAME;
}
}
break;
}
case VCDINFO_ITEM_TYPE_NOTFOUND:
LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
break;
case VCDINFO_ITEM_TYPE_SPAREID2:
LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
break;
case VCDINFO_ITEM_TYPE_LID:
LOG_ERR( "LID in PBC -- not supposed to happen" );
break;
default:
;
}
/* FIXME: Should handle autowait ... */
return READ_ERROR;
}
/*
Get the next play-item in the list given in the LIDs. Note play-item
here refers to list of play-items for a single LID It shouldn't be
confused with a user's list of favorite things to play or the
"next" field of a LID which moves us to a different LID.
*/
bool
vcdplayer_inc_play_item( input_thread_t *p_input )
{
thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
int noi;
dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
if ( NULL == p_vcd || NULL == p_vcd->pxd.pld ) return false;
noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
if ( noi <= 0 ) return false;
/* Handle delays like autowait or wait here? */
p_vcd->pdi++;
if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return false;
else {
uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld,
p_vcd->pdi);
vcdinfo_itemid_t trans_itemid;
if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false;
vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
dbg_print(INPUT_DBG_PBC, " play-item[%d]: %s",
p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
return VLC_SUCCESS == VCDPlay( p_input, trans_itemid );
}
}
/*****************************************************************************
* Copyright (C) 2003 Rocky Bernstein (for VideoLAN)
* $Id: vcdplayer.h,v 1.1 2003/10/04 18:55:13 gbazin Exp $
*
* Authors: Rocky Bernstein <rocky@panix.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/* VCD Player header. More or less media-player independent */
#ifndef _VCDPLAYER_H_
#define _VCDPLAYER_H_
#include <libvcd/info.h>
#define INPUT_DBG_MRL 1
#define INPUT_DBG_EXT 2 /* Calls from external routines */
#define INPUT_DBG_CALL 4 /* all calls */
#define INPUT_DBG_LSN 8 /* LSN changes */
#define INPUT_DBG_PBC 16 /* Playback control */
#define INPUT_DBG_CDIO 32 /* Debugging from CDIO */
#define INPUT_DBG_SEEK 64 /* Seeks to set location */
#define INPUT_DBG_STILL 128 /* Still-frame */
#define INPUT_DBG_VCDINFO 256 /* Debugging from VCDINFO */
#define INPUT_DEBUG 1
#if INPUT_DEBUG
#define dbg_print(mask, s, args...) \
if (p_vcd->i_debug & mask) \
msg_Dbg(p_input, "%s: "s, __func__ , ##args)
#else
#define dbg_print(mask, s, args...)
#endif
#define LOG_ERR(args...) msg_Err( p_input, args )
/* vcdplayer_read return status */
typedef enum {
READ_BLOCK,
READ_STILL_FRAME,
READ_ERROR,
READ_END,
} vcdplayer_read_status_t;
/*****************************************************************************
* thread_vcd_data_t: VCD information
*****************************************************************************/
typedef struct thread_vcd_data_s
{
vcdinfo_obj_t *vcd; /* CD device descriptor */
int in_still; /* 0 if not in still,
-2 if in infinite loop
-5 if a still but haven't
read wait time yet
>0 number of seconds yet to
wait */
unsigned int num_tracks; /* Nb of tracks (titles) */
unsigned int num_segments; /* Nb of segments */
unsigned int num_entries; /* Nb of entries */
unsigned int num_lids; /* Nb of List IDs */
vcdinfo_itemid_t play_item; /* play-item, VCDPLAYER_BAD_ENTRY
if none */
int cur_lid; /* LID that play item is in. Implies
PBC is on. VCDPLAYER_BAD_ENTRY if
not none or not in PBC */
PsdListDescriptor pxd; /* If PBC is on, the relevant
PSD/PLD */
int pdi; /* current pld index of pxd. -1 if
no index*/
vcdinfo_itemid_t loop_item; /* Where do we loop back to?
Meaningful only in a selection
list */
int loop_count; /* # of times play-item has been
played. Meaningful only in a
selection list. */
track_t cur_track; /* Current track number */
lsn_t cur_lsn; /* Current logical sector number */
lsn_t end_lsn; /* LSN of end of current
entry/segment/track. */
lsn_t origin_lsn; /* LSN of start of seek/slider */
lsn_t * p_sectors; /* Track sectors */
lsn_t * p_entries; /* Entry points */
lsn_t * p_segments; /* Segments */
bool b_valid_ep; /* Valid entry points flag */
vlc_bool_t b_end_of_track; /* If the end of track was reached */
int i_debug; /* Debugging mask */
} thread_vcd_data_t;
bool vcdplayer_inc_play_item( input_thread_t *p_input );
bool vcdplayer_pbc_is_on(const thread_vcd_data_t *p_this);
vcdplayer_read_status_t vcdplayer_pbc_nav ( input_thread_t * p_input );
vcdplayer_read_status_t vcdplayer_non_pbc_nav ( input_thread_t * p_input );
lid_t vcdplayer_selection2lid ( input_thread_t *p_input, int entry_num ) ;
#endif /* _VCDPLAYER_H_ */
/*
* Local variables:
* c-file-style: "gnu"
* tab-width: 8
* indent-tabs-mode: nil
* End:
*/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment