1 /*****************************************************************************
2 * vcd.c : VCD input module for vlc
3 *****************************************************************************
4 * Copyright (C) 2000 VideoLAN
5 * $Id: vcd.c,v 1.13 2002/07/31 20:56:52 sam 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>
38 #include <sys/types.h>
44 # include <io.h> /* read() */
46 # include <sys/uio.h> /* struct iovec */
50 # include "input_iovec.h"
53 #include "input_vcd.h"
54 #include "cdrom_tools.h"
56 /* how many blocks VCDRead will read in each loop */
57 #define VCD_BLOCKS_ONCE 20
58 #define VCD_DATA_ONCE (VCD_BLOCKS_ONCE * VCD_DATA_SIZE)
60 /*****************************************************************************
62 *****************************************************************************/
63 static int VCDOpen ( vlc_object_t * );
64 static void VCDClose ( vlc_object_t * );
65 static int VCDRead ( input_thread_t *, byte_t *, size_t );
66 static void VCDSeek ( input_thread_t *, off_t );
67 static int VCDSetArea ( input_thread_t *, input_area_t * );
68 static int VCDSetProgram ( input_thread_t *, pgrm_descriptor_t * );
70 /*****************************************************************************
72 *****************************************************************************/
74 set_description( _("VCD input module") );
75 set_capability( "access", 80 );
76 set_callbacks( VCDOpen, VCDClose );
77 add_shortcut( "svcd" );
81 * Data reading functions
84 /*****************************************************************************
86 *****************************************************************************/
87 static int VCDOpen( vlc_object_t *p_this )
89 input_thread_t * p_input = (input_thread_t *)p_this;
94 struct stat stat_info;
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;
106 /* parse the options passed in command line : */
107 psz_orig = psz_parser = psz_source = strdup( p_input->psz_name );
114 while( *psz_parser && *psz_parser != '@' )
119 if( *psz_parser == '@' )
125 i_title = (int)strtol( psz_parser, &psz_next, 10 );
128 psz_parser = psz_next + 1;
129 i_chapter = (int)strtol( psz_parser, &psz_next, 10 );
132 i_title = i_title ? i_title : 1;
133 i_chapter = i_chapter ? i_chapter : 1;
138 if( !p_input->psz_access )
143 psz_source = config_GetPsz( p_input, "vcd" );
146 /* test the type of file given */
148 if( stat( psz_source, &stat_info ) == -1 )
150 msg_Err( p_input, "cannot stat() source `%s' (%s)",
151 psz_source, strerror(errno));
155 if( !S_ISBLK(stat_info.st_mode) && !S_ISCHR(stat_info.st_mode))
157 msg_Warn( p_input, "vcd module discarded (not a valid drive)" );
162 p_vcd = malloc( sizeof(thread_vcd_data_t) );
166 msg_Err( p_input, "out of memory" );
170 p_input->p_access_data = (void *)p_vcd;
172 p_input->i_mtu = VCD_DATA_ONCE;
174 vlc_mutex_lock( &p_input->stream.stream_lock );
176 p_input->stream.b_pace_control = 1;
178 p_input->stream.b_seekable = 1;
179 p_input->stream.p_selected_area->i_size = 0;
180 p_input->stream.p_selected_area->i_tell = 0;
182 vlc_mutex_unlock( &p_input->stream.stream_lock );
184 p_vcd->i_handle = open( psz_source, O_RDONLY | O_NONBLOCK );
186 if( p_vcd->i_handle == -1 )
188 msg_Err( p_input, "could not open %s\n", psz_source );
193 /* We read the Table Of Content information */
194 p_vcd->nb_tracks = ioctl_GetTrackCount( p_vcd->i_handle,
196 if( p_vcd->nb_tracks < 0 )
198 msg_Err( p_input, "unable to count tracks" );
199 close( p_vcd->i_handle );
203 else if( p_vcd->nb_tracks <= 1 )
205 msg_Err( p_input, "no movie tracks found" );
206 close( p_vcd->i_handle );
211 p_vcd->p_sectors = ioctl_GetSectors( p_vcd->i_handle,
213 if( p_vcd->p_sectors == NULL )
215 input_BuffersEnd( p_input, p_input->p_method_data );
216 close( p_vcd->i_handle );
221 /* Set stream and area data */
222 vlc_mutex_lock( &p_input->stream.stream_lock );
224 /* Initialize ES structures */
225 input_InitStream( p_input, sizeof( stream_ps_data_t ) );
227 /* disc input method */
228 p_input->stream.i_method = INPUT_METHOD_VCD;
230 #define area p_input->stream.pp_areas
231 for( i = 1 ; i <= p_vcd->nb_tracks - 1 ; i++ )
233 input_AddArea( p_input );
235 /* Titles are Program Chains */
238 /* Absolute start offset and size */
239 area[i]->i_start = (off_t)p_vcd->p_sectors[i] * (off_t)VCD_DATA_SIZE;
240 area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
241 * (off_t)VCD_DATA_SIZE;
243 /* Number of chapters */
244 area[i]->i_part_nb = 0; // will be the entry points
247 area[i]->i_plugin_data = p_vcd->p_sectors[i];
251 p_area = p_input->stream.pp_areas[i_title];
253 VCDSetArea( p_input, p_area );
255 vlc_mutex_unlock( &p_input->stream.stream_lock );
257 p_input->psz_demux = "ps";
262 /*****************************************************************************
263 * VCDClose: closes vcd
264 *****************************************************************************/
265 static void VCDClose( vlc_object_t *p_this )
267 input_thread_t * p_input = (input_thread_t *)p_this;
268 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
270 close( p_vcd->i_handle );
274 /*****************************************************************************
275 * VCDRead: reads from the VCD into PES packets.
276 *****************************************************************************
277 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
279 *****************************************************************************/
280 static int VCDRead( input_thread_t * p_input, byte_t * p_buffer,
283 thread_vcd_data_t * p_vcd;
287 byte_t p_last_sector[ VCD_DATA_SIZE ];
289 p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
293 /* Compute the number of blocks we have to read */
295 i_blocks = i_len / VCD_DATA_SIZE;
297 for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
299 if ( ioctl_ReadSector( p_vcd->i_handle, p_vcd->i_sector,
300 p_buffer + i_index * VCD_DATA_SIZE ) < 0 )
302 msg_Err( p_input, "could not read sector %d", p_vcd->i_sector );
307 if ( p_vcd->i_sector == p_vcd->p_sectors[p_vcd->i_track + 1] )
309 input_area_t *p_area;
311 if ( p_vcd->i_track >= p_vcd->nb_tracks - 1 )
314 p_area = p_input->stream.pp_areas[
315 p_input->stream.p_selected_area->i_id + 1 ];
317 msg_Dbg( p_input, "new title" );
320 VCDSetArea( p_input, p_area );
323 i_read += VCD_DATA_SIZE;
326 if ( i_len % VCD_DATA_SIZE ) /* this should not happen */
328 if ( ioctl_ReadSector( p_vcd->i_handle, p_vcd->i_sector,
329 p_last_sector ) < 0 )
331 msg_Err( p_input, "could not read sector %d", p_vcd->i_sector );
335 p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * VCD_DATA_SIZE,
336 p_last_sector, i_len % VCD_DATA_SIZE );
337 i_read += i_len % VCD_DATA_SIZE;
340 p_input->stream.p_selected_area->i_tell =
341 (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
342 - p_input->stream.p_selected_area->i_start;
348 /*****************************************************************************
349 * VCDSetProgram: Does nothing since a VCD is mono_program
350 *****************************************************************************/
351 static int VCDSetProgram( input_thread_t * p_input,
352 pgrm_descriptor_t * p_program)
358 /*****************************************************************************
359 * VCDSetArea: initialize input data for title x, chapter y.
360 * It should be called for each user navigation request.
361 ****************************************************************************/
362 static int VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
364 thread_vcd_data_t * p_vcd;
366 p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
368 /* we can't use the interface slider until initilization is complete */
369 p_input->stream.b_seekable = 0;
371 if( p_area != p_input->stream.p_selected_area )
373 /* Reset the Chapter position of the current title */
374 p_input->stream.p_selected_area->i_part = 1;
375 p_input->stream.p_selected_area->i_tell = 0;
377 /* Change the default area */
378 p_input->stream.p_selected_area = p_area;
380 /* Change the current track */
381 /* The first track is not a valid one */
382 p_vcd->i_track = p_area->i_id;
383 p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track];
386 /* warn interface that something has changed */
387 p_input->stream.b_seekable = 1;
388 p_input->stream.b_changed = 1;
394 /****************************************************************************
396 ****************************************************************************/
397 static void VCDSeek( input_thread_t * p_input, off_t i_off )
399 thread_vcd_data_t * p_vcd;
401 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
403 p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track]
404 + i_off / (off_t)VCD_DATA_SIZE;
406 p_input->stream.p_selected_area->i_tell =
407 (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
408 - p_input->stream.p_selected_area->i_start;