1 /*****************************************************************************
2 * vcd.c : VCD input module for vlc
3 *****************************************************************************
4 * Copyright (C) 2000 VideoLAN
5 * $Id: vcd.c,v 1.7 2002/10/15 19:56:59 gbazin Exp $
7 * Author: Johan Bilien <jobi@via.ecp.fr>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
31 #include <vlc/input.h>
33 #include "../../demux/mpeg/system.h"
43 /* how many blocks VCDRead will read in each loop */
44 #define VCD_BLOCKS_ONCE 20
45 #define VCD_DATA_ONCE (VCD_BLOCKS_ONCE * VCD_DATA_SIZE)
47 /*****************************************************************************
48 * thread_vcd_data_t: VCD information
49 *****************************************************************************/
50 typedef struct thread_vcd_data_s
52 vcddev_t *vcddev; /* vcd device descriptor */
53 int nb_tracks; /* Nb of tracks (titles) */
54 int i_track; /* Current track */
55 int i_sector; /* Current Sector */
56 int * p_sectors; /* Track sectors */
57 vlc_bool_t b_end_of_track; /* If the end of track was reached */
61 /*****************************************************************************
63 *****************************************************************************/
64 static int VCDOpen ( vlc_object_t * );
65 static void VCDClose ( vlc_object_t * );
66 static int VCDRead ( input_thread_t *, byte_t *, size_t );
67 static void VCDSeek ( input_thread_t *, off_t );
68 static int VCDSetArea ( input_thread_t *, input_area_t * );
69 static int VCDSetProgram ( input_thread_t *, pgrm_descriptor_t * );
71 /*****************************************************************************
73 *****************************************************************************/
75 set_description( _("VCD input module") );
76 set_capability( "access", 80 );
77 set_callbacks( VCDOpen, VCDClose );
78 add_shortcut( "svcd" );
82 * Data reading functions
85 /*****************************************************************************
87 *****************************************************************************/
88 static int VCDOpen( vlc_object_t *p_this )
90 input_thread_t * p_input = (input_thread_t *)p_this;
95 thread_vcd_data_t * p_vcd;
97 input_area_t * p_area;
101 p_input->pf_read = VCDRead;
102 p_input->pf_seek = VCDSeek;
103 p_input->pf_set_area = VCDSetArea;
104 p_input->pf_set_program = VCDSetProgram;
107 /* On Win32 we want the VCD access plugin to be explicitly requested,
108 * we end up with lots of problems otherwise */
109 if( !p_input->psz_access || !*p_input->psz_access ) return( -1 );
112 /* parse the options passed in command line : */
113 psz_orig = psz_parser = psz_source = strdup( p_input->psz_name );
120 while( *psz_parser && *psz_parser != '@' )
125 if( *psz_parser == '@' )
131 i_title = (int)strtol( psz_parser, &psz_next, 10 );
134 psz_parser = psz_next + 1;
135 i_chapter = (int)strtol( psz_parser, &psz_next, 10 );
138 i_title = i_title ? i_title : 1;
139 i_chapter = i_chapter ? i_chapter : 1;
144 if( !p_input->psz_access )
149 psz_source = config_GetPsz( p_input, "vcd" );
150 if( !psz_source ) return -1;
153 p_vcd = malloc( sizeof(thread_vcd_data_t) );
157 msg_Err( p_input, "out of memory" );
162 p_input->p_access_data = (void *)p_vcd;
164 p_input->i_mtu = VCD_DATA_ONCE;
166 vlc_mutex_lock( &p_input->stream.stream_lock );
168 p_input->stream.b_pace_control = 1;
170 p_input->stream.b_seekable = 1;
171 p_input->stream.p_selected_area->i_size = 0;
172 p_input->stream.p_selected_area->i_tell = 0;
174 vlc_mutex_unlock( &p_input->stream.stream_lock );
176 if( !(p_vcd->vcddev = ioctl_Open( p_this, psz_source )) )
178 msg_Err( p_input, "could not open %s", psz_source );
184 /* We read the Table Of Content information */
185 p_vcd->nb_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
186 p_vcd->vcddev, &p_vcd->p_sectors );
188 if( p_vcd->nb_tracks < 0 )
189 msg_Err( p_input, "unable to count tracks" );
190 else if( p_vcd->nb_tracks <= 1 )
191 msg_Err( p_input, "no movie tracks found" );
192 if( p_vcd->nb_tracks <= 1)
194 //input_BuffersEnd( p_input, p_input->p_method_data ); /* ??? */
195 ioctl_Close( p_this, p_vcd->vcddev );
200 /* Set stream and area data */
201 vlc_mutex_lock( &p_input->stream.stream_lock );
203 /* Initialize ES structures */
204 input_InitStream( p_input, sizeof( stream_ps_data_t ) );
206 /* disc input method */
207 p_input->stream.i_method = INPUT_METHOD_VCD;
209 #define area p_input->stream.pp_areas
210 for( i = 1 ; i <= p_vcd->nb_tracks - 1 ; i++ )
212 input_AddArea( p_input );
214 /* Titles are Program Chains */
217 /* Absolute start offset and size */
218 area[i]->i_start = (off_t)p_vcd->p_sectors[i] * (off_t)VCD_DATA_SIZE;
219 area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
220 * (off_t)VCD_DATA_SIZE;
222 /* Number of chapters */
223 area[i]->i_part_nb = 0; /* will be the entry points */
226 area[i]->i_plugin_data = p_vcd->p_sectors[i];
230 p_area = p_input->stream.pp_areas[i_title];
232 VCDSetArea( p_input, p_area );
234 vlc_mutex_unlock( &p_input->stream.stream_lock );
236 p_input->psz_demux = "ps";
241 /*****************************************************************************
242 * VCDClose: closes vcd
243 *****************************************************************************/
244 static void VCDClose( vlc_object_t *p_this )
246 input_thread_t * p_input = (input_thread_t *)p_this;
247 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
249 ioctl_Close( p_this, p_vcd->vcddev );
253 /*****************************************************************************
254 * VCDRead: reads from the VCD into PES packets.
255 *****************************************************************************
256 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
258 *****************************************************************************/
259 static int VCDRead( input_thread_t * p_input, byte_t * p_buffer,
262 thread_vcd_data_t * p_vcd;
266 byte_t p_last_sector[ VCD_DATA_SIZE ];
268 p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
272 /* Compute the number of blocks we have to read */
274 i_blocks = i_len / VCD_DATA_SIZE;
276 for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
278 if ( ioctl_ReadSector( VLC_OBJECT(p_input), p_vcd->vcddev,
279 p_vcd->i_sector, p_buffer + i_index * VCD_DATA_SIZE ) < 0 )
281 msg_Err( p_input, "could not read sector %d", p_vcd->i_sector );
286 if ( p_vcd->i_sector == p_vcd->p_sectors[p_vcd->i_track + 1] )
288 input_area_t *p_area;
290 if ( p_vcd->i_track >= p_vcd->nb_tracks - 1 )
293 p_area = p_input->stream.pp_areas[
294 p_input->stream.p_selected_area->i_id + 1 ];
296 msg_Dbg( p_input, "new title" );
299 VCDSetArea( p_input, p_area );
302 i_read += VCD_DATA_SIZE;
305 if ( i_len % VCD_DATA_SIZE ) /* this should not happen */
307 if ( ioctl_ReadSector( VLC_OBJECT(p_input), p_vcd->vcddev,
308 p_vcd->i_sector, p_last_sector ) < 0 )
310 msg_Err( p_input, "could not read sector %d", p_vcd->i_sector );
314 p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * VCD_DATA_SIZE,
315 p_last_sector, i_len % VCD_DATA_SIZE );
316 i_read += i_len % VCD_DATA_SIZE;
319 p_input->stream.p_selected_area->i_tell =
320 (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
321 - p_input->stream.p_selected_area->i_start;
327 /*****************************************************************************
328 * VCDSetProgram: Does nothing since a VCD is mono_program
329 *****************************************************************************/
330 static int VCDSetProgram( input_thread_t * p_input,
331 pgrm_descriptor_t * p_program)
337 /*****************************************************************************
338 * VCDSetArea: initialize input data for title x, chapter y.
339 * It should be called for each user navigation request.
340 ****************************************************************************/
341 static int VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
343 thread_vcd_data_t * p_vcd;
345 p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
347 /* we can't use the interface slider until initilization is complete */
348 p_input->stream.b_seekable = 0;
350 if( p_area != p_input->stream.p_selected_area )
352 /* Reset the Chapter position of the current title */
353 p_input->stream.p_selected_area->i_part = 1;
354 p_input->stream.p_selected_area->i_tell = 0;
356 /* Change the default area */
357 p_input->stream.p_selected_area = p_area;
359 /* Change the current track */
360 /* The first track is not a valid one */
361 p_vcd->i_track = p_area->i_id;
362 p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track];
365 /* warn interface that something has changed */
366 p_input->stream.b_seekable = 1;
367 p_input->stream.b_changed = 1;
373 /****************************************************************************
375 ****************************************************************************/
376 static void VCDSeek( input_thread_t * p_input, off_t i_off )
378 thread_vcd_data_t * p_vcd;
380 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
382 p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track]
383 + i_off / (off_t)VCD_DATA_SIZE;
385 p_input->stream.p_selected_area->i_tell =
386 (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
387 - p_input->stream.p_selected_area->i_start;