1 /*****************************************************************************
2 * vcd.c : VCD input module for vlc
3 *****************************************************************************
4 * Copyright (C) 2000 VideoLAN
5 * $Id: vcd.c,v 1.24 2003/08/10 13:35:03 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 i_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 int i_entries_nb; /* Number of entry points */
58 int * p_entries; /* Entry points */
59 vlc_bool_t b_valid_ep; /* Valid entry points flag */
60 vlc_bool_t b_end_of_track; /* If the end of track was reached */
64 /*****************************************************************************
66 *****************************************************************************/
67 static int VCDOpen ( vlc_object_t * );
68 static void VCDClose ( vlc_object_t * );
69 static int VCDRead ( input_thread_t *, byte_t *, size_t );
70 static void VCDSeek ( input_thread_t *, off_t );
71 static int VCDSetArea ( input_thread_t *, input_area_t * );
72 static int VCDSetProgram ( input_thread_t *, pgrm_descriptor_t * );
73 static int VCDEntryPoints ( input_thread_t * );
75 /*****************************************************************************
77 *****************************************************************************/
79 set_description( _("VCD input") );
80 set_capability( "access", 80 );
81 set_callbacks( VCDOpen, VCDClose );
82 add_shortcut( "svcd" );
86 * Data reading functions
89 /*****************************************************************************
91 *****************************************************************************/
92 static int VCDOpen( vlc_object_t *p_this )
94 input_thread_t * p_input = (input_thread_t *)p_this;
99 thread_vcd_data_t * p_vcd;
101 input_area_t * p_area;
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 > 0 ? i_title : 1;
133 i_chapter = i_chapter > 0 ? i_chapter : 1;
138 if( !p_input->psz_access )
143 psz_source = config_GetPsz( p_input, "vcd" );
144 if( !psz_source ) return -1;
148 if( !(vcddev = ioctl_Open( p_this, psz_source )) )
150 msg_Warn( p_input, "could not open %s", psz_source );
155 p_vcd = malloc( sizeof(thread_vcd_data_t) );
158 msg_Err( p_input, "out of memory" );
164 p_vcd->vcddev = vcddev;
165 p_input->p_access_data = (void *)p_vcd;
167 p_input->i_mtu = VCD_DATA_ONCE;
169 vlc_mutex_lock( &p_input->stream.stream_lock );
170 p_input->stream.b_pace_control = 1;
171 p_input->stream.b_seekable = 1;
172 p_input->stream.p_selected_area->i_size = 0;
173 p_input->stream.p_selected_area->i_tell = 0;
174 vlc_mutex_unlock( &p_input->stream.stream_lock );
176 /* We read the Table Of Content information */
177 p_vcd->i_nb_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
178 p_vcd->vcddev, &p_vcd->p_sectors );
179 if( p_vcd->i_nb_tracks < 0 )
180 msg_Err( p_input, "unable to count tracks" );
181 else if( p_vcd->i_nb_tracks <= 1 )
182 msg_Err( p_input, "no movie tracks found" );
183 if( p_vcd->i_nb_tracks <= 1)
185 ioctl_Close( p_this, p_vcd->vcddev );
190 /* Allocate the entry points table */
191 p_vcd->p_entries = malloc( p_vcd->i_nb_tracks * sizeof( int ) );
193 if( p_vcd->p_entries == NULL )
195 msg_Err( p_input, "not enough memory" );
196 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 p_input->stream.i_area_nb = 1;
211 #define area p_input->stream.pp_areas
212 for( i = 1 ; i < p_vcd->i_nb_tracks; i++ )
214 /* Titles are Program Chains */
215 input_AddArea( p_input, i, 1 );
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 /* Default Chapter */
225 /* i_plugin_data is used to store which entry point is the first
226 * of the track (area) */
227 area[i]->i_plugin_data = 0;
231 p_area = p_input->stream.pp_areas[__MIN(i_title,p_vcd->i_nb_tracks -1)];
233 p_vcd->b_valid_ep = 1;
234 if( VCDEntryPoints( p_input ) < 0 )
236 msg_Warn( p_input, "could not read entry points, will not use them" );
237 p_vcd->b_valid_ep = 0;
240 VCDSetArea( p_input, p_area );
242 vlc_mutex_unlock( &p_input->stream.stream_lock );
244 if( !p_input->psz_demux || !*p_input->psz_demux )
246 p_input->psz_demux = "ps";
249 p_input->pf_read = VCDRead;
250 p_input->pf_seek = VCDSeek;
251 p_input->pf_set_area = VCDSetArea;
252 p_input->pf_set_program = VCDSetProgram;
257 /*****************************************************************************
258 * VCDClose: closes vcd
259 *****************************************************************************/
260 static void VCDClose( vlc_object_t *p_this )
262 input_thread_t * p_input = (input_thread_t *)p_this;
263 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
265 ioctl_Close( p_this, p_vcd->vcddev );
269 /*****************************************************************************
270 * VCDRead: reads from the VCD into PES packets.
271 *****************************************************************************
272 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
274 *****************************************************************************/
275 static int VCDRead( input_thread_t * p_input, byte_t * p_buffer,
278 thread_vcd_data_t * p_vcd;
282 byte_t p_last_sector[ VCD_DATA_SIZE ];
284 p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
288 /* Compute the number of blocks we have to read */
290 i_blocks = i_len / VCD_DATA_SIZE;
292 for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
294 if ( ioctl_ReadSectors( VLC_OBJECT(p_input), p_vcd->vcddev,
295 p_vcd->i_sector, p_buffer + i_index * VCD_DATA_SIZE, 1,
298 msg_Err( p_input, "could not read sector %d", p_vcd->i_sector );
303 if ( p_vcd->i_sector == p_vcd->p_sectors[p_vcd->i_track + 1] )
305 input_area_t *p_area;
307 if ( p_vcd->i_track >= p_vcd->i_nb_tracks - 1 )
310 vlc_mutex_lock( &p_input->stream.stream_lock );
311 p_area = p_input->stream.pp_areas[
312 p_input->stream.p_selected_area->i_id + 1 ];
314 msg_Dbg( p_input, "new title" );
317 VCDSetArea( p_input, p_area );
318 vlc_mutex_unlock( &p_input->stream.stream_lock );
322 else if( p_vcd->b_valid_ep &&
323 /* FIXME kludge so that read does not update chapter
324 * when a manual chapter change was requested and not
325 * yet accomplished */
326 !p_input->stream.p_new_area )
330 vlc_mutex_lock( &p_input->stream.stream_lock );
331 i_entry = p_input->stream.p_selected_area->i_plugin_data
332 /* 1st entry point of the track (area)*/
333 + p_input->stream.p_selected_area->i_part - 1;
334 if( i_entry + 1 < p_vcd->i_entries_nb &&
335 p_vcd->i_sector >= p_vcd->p_entries[i_entry + 1] )
339 msg_Dbg( p_input, "new chapter" );
340 p_input->stream.p_selected_area->i_part ++;
342 /* Update the navigation variables without triggering
344 val.i_int = p_input->stream.p_selected_area->i_part;
345 var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val, NULL );
347 vlc_mutex_unlock( &p_input->stream.stream_lock );
350 i_read += VCD_DATA_SIZE;
353 if ( i_len % VCD_DATA_SIZE ) /* this should not happen */
355 if ( ioctl_ReadSectors( VLC_OBJECT(p_input), p_vcd->vcddev,
356 p_vcd->i_sector, p_last_sector, 1, VCD_TYPE ) < 0 )
358 msg_Err( p_input, "could not read sector %d", p_vcd->i_sector );
362 p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * VCD_DATA_SIZE,
363 p_last_sector, i_len % VCD_DATA_SIZE );
364 i_read += i_len % VCD_DATA_SIZE;
370 /*****************************************************************************
371 * VCDSetProgram: Does nothing since a VCD is mono_program
372 *****************************************************************************/
373 static int VCDSetProgram( input_thread_t * p_input,
374 pgrm_descriptor_t * p_program)
379 /*****************************************************************************
380 * VCDSetArea: initialize input data for title x, chapter y.
381 * It should be called for each user navigation request.
382 ****************************************************************************/
383 static int VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
385 thread_vcd_data_t * p_vcd;
388 p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
390 /* we can't use the interface slider until initilization is complete */
391 p_input->stream.b_seekable = 0;
393 if( p_area != p_input->stream.p_selected_area )
397 /* Reset the Chapter position of the current title */
398 p_input->stream.p_selected_area->i_part = 1;
399 p_input->stream.p_selected_area->i_tell = 0;
401 /* Change the default area */
402 p_input->stream.p_selected_area = p_area;
404 /* Change the current track */
405 /* The first track is not a valid one */
406 p_vcd->i_track = p_area->i_id;
407 p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track];
409 /* Update the navigation variables without triggering a callback */
410 val.i_int = p_area->i_id;
411 var_Change( p_input, "title", VLC_VAR_SETVALUE, &val, NULL );
412 var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
413 for( i = 1; i <= p_area->i_part_nb; i++ )
416 var_Change( p_input, "chapter", VLC_VAR_ADDCHOICE, &val, NULL );
420 if( p_vcd->b_valid_ep )
422 int i_entry = p_area->i_plugin_data /* 1st entry point of
424 + p_area->i_part - 1;
425 p_vcd->i_sector = p_vcd->p_entries[i_entry];
428 p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track];
430 p_input->stream.p_selected_area->i_tell =
431 (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
432 - p_input->stream.p_selected_area->i_start;
434 /* warn interface that something has changed */
435 p_input->stream.b_seekable = 1;
436 p_input->stream.b_changed = 1;
438 /* Update the navigation variables without triggering a callback */
439 val.i_int = p_area->i_part;
440 var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val, NULL );
445 /****************************************************************************
447 ****************************************************************************/
448 static void VCDSeek( input_thread_t * p_input, off_t i_off )
450 thread_vcd_data_t * p_vcd;
451 unsigned int i_index;
453 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
455 p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track]
456 + i_off / (off_t)VCD_DATA_SIZE;
458 vlc_mutex_lock( &p_input->stream.stream_lock );
459 #define p_area p_input->stream.p_selected_area
461 if( p_vcd->b_valid_ep )
463 for( i_index = 2 ; i_index <= p_area->i_part_nb; i_index ++ )
465 if( p_vcd->i_sector < p_vcd->p_entries[p_area->i_plugin_data
470 p_area->i_part = i_index - 1;
472 /* Update the navigation variables without triggering
474 val.i_int = p_area->i_part;
475 var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val, NULL );
482 p_input->stream.p_selected_area->i_tell =
483 (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
484 - p_input->stream.p_selected_area->i_start;
485 vlc_mutex_unlock( &p_input->stream.stream_lock );
488 /*****************************************************************************
489 * VCDEntryPoints: Reads the information about the entry points on the disc.
490 *****************************************************************************/
491 static int VCDEntryPoints( input_thread_t * p_input )
493 thread_vcd_data_t * p_vcd;
495 entries_sect_t entries;
497 int i, i_entry_index = 0;
498 int i_previous_track = -1;
500 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
502 p_sector = malloc( VCD_DATA_SIZE * sizeof( byte_t ) );
503 if( p_sector == NULL )
505 msg_Err( p_input, "not enough memory for entry points treatment" );
509 if( ioctl_ReadSectors( VLC_OBJECT(p_input), p_vcd->vcddev,
510 VCD_ENTRIES_SECTOR, p_sector, 1, VCD_TYPE ) < 0 )
512 msg_Err( p_input, "could not read entry points sector" );
517 memcpy( &entries, p_sector, CD_SECTOR_SIZE );
520 if( (i_nb = U16_AT( &entries.i_entries_nb )) > 500 )
522 msg_Err( p_input, "invalid entry points number" );
526 p_vcd->p_entries = malloc( sizeof( int ) * i_nb );
527 if( p_vcd->p_entries == NULL )
529 msg_Err( p_input, "not enough memory for entry points treatment" );
533 if( strncmp( entries.psz_id, "ENTRYVCD", sizeof( entries.psz_id ) )
534 && strncmp( entries.psz_id, "ENTRYSVD", sizeof( entries.psz_id ) ))
536 msg_Err( p_input, "unrecognized entry points format" );
537 free( p_vcd->p_entries );
541 p_vcd->i_entries_nb = 0;
543 #define i_track BCD_TO_BIN(entries.entry[i].i_track)
544 /* Reset the i_part_nb for each track */
545 for( i = 0 ; i < i_nb ; i++ )
547 if( i_track <= p_input->stream.i_area_nb )
549 p_input->stream.pp_areas[i_track-1]->i_part_nb = 0;
553 for( i = 0 ; i < i_nb ; i++ )
555 if( i_track <= p_input->stream.i_area_nb )
557 p_vcd->p_entries[i_entry_index] =
558 (MSF_TO_LBA2( BCD_TO_BIN( entries.entry[i].msf.minute ),
559 BCD_TO_BIN( entries.entry[i].msf.second ),
560 BCD_TO_BIN( entries.entry[i].msf.frame ) ));
561 p_input->stream.pp_areas[i_track-1]->i_part_nb ++;
562 /* if this entry belongs to a new track */
563 if( i_track != i_previous_track )
565 /* i_plugin_data is used to store the first entry of the area*/
566 p_input->stream.pp_areas[i_track-1]->i_plugin_data =
568 i_previous_track = i_track;
570 msg_Dbg( p_input, "entry point %i begins at LBA: %i",
571 i_entry_index, p_vcd->p_entries[i_entry_index] );
574 p_vcd->i_entries_nb ++;
577 msg_Warn( p_input, "wrong track number found in entry points" );