1 /*****************************************************************************
2 * vcd.c : VCD input module for vlc
3 *****************************************************************************
4 * Copyright (C) 2000-2004 VideoLAN
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 *****************************************************************************/
30 #include <vlc/input.h>
34 /*****************************************************************************
36 *****************************************************************************/
37 static int Open ( vlc_object_t * );
38 static void Close( vlc_object_t * );
41 set_description( _("VCD input") );
42 set_capability( "access", 80 );
43 set_callbacks( Open, Close );
45 add_usage_hint( N_("[vcd:][device][@[title][,[chapter]]]") );
46 add_shortcut( "svcd" );
49 /*****************************************************************************
51 *****************************************************************************/
53 /* how many blocks VCDRead will read in each loop */
54 #define VCD_BLOCKS_ONCE 20
55 #define VCD_DATA_ONCE (VCD_BLOCKS_ONCE * VCD_DATA_SIZE)
59 vcddev_t *vcddev; /* vcd device descriptor */
60 int i_nb_tracks; /* Nb of tracks (titles) */
61 int i_track; /* Current track */
62 int i_sector; /* Current Sector */
63 int * p_sectors; /* Track sectors */
64 int i_entries_nb; /* Number of entry points */
65 int * p_entries; /* Entry points */
66 vlc_bool_t b_valid_ep; /* Valid entry points flag */
67 vlc_bool_t b_end_of_track; /* If the end of track was reached */
70 static int VCDRead ( input_thread_t *, byte_t *, size_t );
71 static void VCDSeek ( input_thread_t *, off_t );
72 static int VCDSetArea ( input_thread_t *, input_area_t * );
73 static int VCDSetProgram ( input_thread_t *, pgrm_descriptor_t * );
74 static int VCDEntryPoints ( input_thread_t * );
76 /*****************************************************************************
78 *****************************************************************************/
79 static int Open( vlc_object_t *p_this )
81 input_thread_t *p_input = (input_thread_t *)p_this;
83 char *psz_dup = strdup( p_input->psz_name );
87 input_area_t * p_area;
92 /* Command line: vcd://[dev_path][@title[,chapter]] */
93 if( ( psz = strchr( psz_dup, '@' ) ) )
97 i_title = strtol( psz, &psz, 0 );
100 i_chapter = strtol( psz, &psz, 0 );
104 if( *psz_dup == '\0' )
108 /* Only when selected */
109 if( *p_input->psz_access == '\0' )
112 psz_dup = var_CreateGetString( p_input, "vcd" );
113 if( *psz_dup == '\0' )
121 if( !(vcddev = ioctl_Open( p_this, psz_dup )) )
123 msg_Warn( p_input, "could not open %s", psz_dup );
128 p_sys = malloc( sizeof(access_sys_t) );
131 msg_Err( p_input, "out of memory" );
137 p_sys->vcddev = vcddev;
138 p_input->p_access_data = (void *)p_sys;
140 p_input->i_mtu = VCD_DATA_ONCE;
142 vlc_mutex_lock( &p_input->stream.stream_lock );
143 p_input->stream.b_pace_control = 1;
144 p_input->stream.b_seekable = 1;
145 p_input->stream.p_selected_area->i_size = 0;
146 p_input->stream.p_selected_area->i_tell = 0;
147 vlc_mutex_unlock( &p_input->stream.stream_lock );
149 /* We read the Table Of Content information */
150 p_sys->i_nb_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
151 p_sys->vcddev, &p_sys->p_sectors );
152 if( p_sys->i_nb_tracks < 0 )
153 msg_Err( p_input, "unable to count tracks" );
154 else if( p_sys->i_nb_tracks <= 1 )
155 msg_Err( p_input, "no movie tracks found" );
156 if( p_sys->i_nb_tracks <= 1)
158 ioctl_Close( p_this, p_sys->vcddev );
163 /* Allocate the entry points table */
164 p_sys->p_entries = malloc( p_sys->i_nb_tracks * sizeof( int ) );
166 if( p_sys->p_entries == NULL )
168 msg_Err( p_input, "not enough memory" );
169 ioctl_Close( p_this, p_sys->vcddev );
173 /* Set stream and area data */
174 vlc_mutex_lock( &p_input->stream.stream_lock );
176 /* Initialize ES structures */
177 input_InitStream( p_input, 0 );
179 /* disc input method */
180 p_input->stream.i_method = INPUT_METHOD_VCD;
182 p_input->stream.i_area_nb = 1;
184 #define area p_input->stream.pp_areas
185 for( i = 1 ; i < p_sys->i_nb_tracks; i++ )
187 /* Titles are Program Chains */
188 input_AddArea( p_input, i, 1 );
190 /* Absolute start offset and size */
191 area[i]->i_start = (off_t)p_sys->p_sectors[i] * (off_t)VCD_DATA_SIZE;
192 area[i]->i_size = (off_t)(p_sys->p_sectors[i+1] - p_sys->p_sectors[i])
193 * (off_t)VCD_DATA_SIZE;
195 /* Default Chapter */
198 /* i_plugin_data is used to store which entry point is the first
199 * of the track (area) */
200 area[i]->i_plugin_data = 0;
204 p_area = p_input->stream.pp_areas[__MIN(i_title,p_sys->i_nb_tracks -1)];
206 p_sys->b_valid_ep = 1;
207 if( VCDEntryPoints( p_input ) < 0 )
209 msg_Warn( p_input, "could not read entry points, will not use them" );
210 p_sys->b_valid_ep = 0;
213 VCDSetArea( p_input, p_area );
215 vlc_mutex_unlock( &p_input->stream.stream_lock );
217 if( !p_input->psz_demux || !*p_input->psz_demux )
219 p_input->psz_demux = "ps";
222 p_input->pf_read = VCDRead;
223 p_input->pf_seek = VCDSeek;
224 p_input->pf_set_area = VCDSetArea;
225 p_input->pf_set_program = VCDSetProgram;
230 /*****************************************************************************
231 * VCDClose: closes vcd
232 *****************************************************************************/
233 static void Close( vlc_object_t *p_this )
235 input_thread_t * p_input = (input_thread_t *)p_this;
236 access_sys_t *p_sys = p_input->p_access_data;
238 ioctl_Close( p_this, p_sys->vcddev );
242 /*****************************************************************************
243 * VCDRead: reads from the VCD into PES packets.
244 *****************************************************************************
245 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
247 *****************************************************************************/
248 static int VCDRead( input_thread_t * p_input, byte_t * p_buffer,
255 byte_t p_last_sector[ VCD_DATA_SIZE ];
257 p_sys = p_input->p_access_data;
261 /* Compute the number of blocks we have to read */
263 i_blocks = i_len / VCD_DATA_SIZE;
265 for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
267 if ( ioctl_ReadSectors( VLC_OBJECT(p_input), p_sys->vcddev,
268 p_sys->i_sector, p_buffer + i_index * VCD_DATA_SIZE, 1,
271 msg_Err( p_input, "could not read sector %d", p_sys->i_sector );
276 if ( p_sys->i_sector == p_sys->p_sectors[p_sys->i_track + 1] )
278 input_area_t *p_area;
280 if ( p_sys->i_track >= p_sys->i_nb_tracks - 1 )
283 vlc_mutex_lock( &p_input->stream.stream_lock );
284 p_area = p_input->stream.pp_areas[
285 p_input->stream.p_selected_area->i_id + 1 ];
287 msg_Dbg( p_input, "new title" );
290 VCDSetArea( p_input, p_area );
291 vlc_mutex_unlock( &p_input->stream.stream_lock );
295 else if( p_sys->b_valid_ep &&
296 /* FIXME kludge so that read does not update chapter
297 * when a manual chapter change was requested and not
298 * yet accomplished */
299 !p_input->stream.p_new_area )
303 vlc_mutex_lock( &p_input->stream.stream_lock );
304 i_entry = p_input->stream.p_selected_area->i_plugin_data
305 /* 1st entry point of the track (area)*/
306 + p_input->stream.p_selected_area->i_part - 1;
307 if( i_entry + 1 < p_sys->i_entries_nb &&
308 p_sys->i_sector >= p_sys->p_entries[i_entry + 1] )
312 msg_Dbg( p_input, "new chapter" );
313 p_input->stream.p_selected_area->i_part ++;
315 /* Update the navigation variables without triggering
317 val.i_int = p_input->stream.p_selected_area->i_part;
318 var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val, NULL );
320 vlc_mutex_unlock( &p_input->stream.stream_lock );
323 i_read += VCD_DATA_SIZE;
326 if ( i_len % VCD_DATA_SIZE ) /* this should not happen */
328 if ( ioctl_ReadSectors( VLC_OBJECT(p_input), p_sys->vcddev,
329 p_sys->i_sector, p_last_sector, 1, VCD_TYPE ) < 0 )
331 msg_Err( p_input, "could not read sector %d", p_sys->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;
343 /*****************************************************************************
344 * VCDSetProgram: Does nothing since a VCD is mono_program
345 *****************************************************************************/
346 static int VCDSetProgram( input_thread_t * p_input,
347 pgrm_descriptor_t * p_program)
352 /*****************************************************************************
353 * VCDSetArea: initialize input data for title x, chapter y.
354 * It should be called for each user navigation request.
355 ****************************************************************************/
356 static int VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
358 access_sys_t *p_sys = p_input->p_access_data;
361 /* we can't use the interface slider until initilization is complete */
362 p_input->stream.b_seekable = 0;
364 if( p_area != p_input->stream.p_selected_area )
368 /* Reset the Chapter position of the current title */
369 p_input->stream.p_selected_area->i_part = 1;
370 p_input->stream.p_selected_area->i_tell = 0;
372 /* Change the default area */
373 p_input->stream.p_selected_area = p_area;
375 /* Change the current track */
376 /* The first track is not a valid one */
377 p_sys->i_track = p_area->i_id;
378 p_sys->i_sector = p_sys->p_sectors[p_sys->i_track];
380 /* Update the navigation variables without triggering a callback */
381 val.i_int = p_area->i_id;
382 var_Change( p_input, "title", VLC_VAR_SETVALUE, &val, NULL );
383 var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
384 for( i = 1; i <= p_area->i_part_nb; i++ )
387 var_Change( p_input, "chapter", VLC_VAR_ADDCHOICE, &val, NULL );
391 if( p_sys->b_valid_ep )
393 int i_entry = p_area->i_plugin_data /* 1st entry point of
395 + p_area->i_part - 1;
396 p_sys->i_sector = p_sys->p_entries[i_entry];
399 p_sys->i_sector = p_sys->p_sectors[p_sys->i_track];
401 p_input->stream.p_selected_area->i_tell =
402 (off_t)p_sys->i_sector * (off_t)VCD_DATA_SIZE
403 - p_input->stream.p_selected_area->i_start;
405 /* warn interface that something has changed */
406 p_input->stream.b_seekable = 1;
407 p_input->stream.b_changed = 1;
409 /* Update the navigation variables without triggering a callback */
410 val.i_int = p_area->i_part;
411 var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val, NULL );
416 /****************************************************************************
418 ****************************************************************************/
419 static void VCDSeek( input_thread_t * p_input, off_t i_off )
421 access_sys_t * p_sys = p_input->p_access_data;
422 unsigned int i_index;
424 p_sys->i_sector = p_sys->p_sectors[p_sys->i_track]
425 + i_off / (off_t)VCD_DATA_SIZE;
427 vlc_mutex_lock( &p_input->stream.stream_lock );
428 #define p_area p_input->stream.p_selected_area
430 if( p_sys->b_valid_ep )
432 for( i_index = 2 ; i_index <= p_area->i_part_nb; i_index ++ )
434 if( p_sys->i_sector < p_sys->p_entries[p_area->i_plugin_data
439 p_area->i_part = i_index - 1;
441 /* Update the navigation variables without triggering
443 val.i_int = p_area->i_part;
444 var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val, NULL );
451 p_input->stream.p_selected_area->i_tell =
452 (off_t)p_sys->i_sector * (off_t)VCD_DATA_SIZE
453 - p_input->stream.p_selected_area->i_start;
454 vlc_mutex_unlock( &p_input->stream.stream_lock );
457 /*****************************************************************************
458 * VCDEntryPoints: Reads the information about the entry points on the disc.
459 *****************************************************************************/
460 static int VCDEntryPoints( input_thread_t * p_input )
462 access_sys_t * p_sys = p_input->p_access_data;
464 entries_sect_t entries;
466 int i, i_entry_index = 0;
467 int i_previous_track = -1;
469 p_sector = malloc( VCD_DATA_SIZE * sizeof( byte_t ) );
470 if( p_sector == NULL )
472 msg_Err( p_input, "not enough memory for entry points treatment" );
476 if( ioctl_ReadSectors( VLC_OBJECT(p_input), p_sys->vcddev,
477 VCD_ENTRIES_SECTOR, p_sector, 1, VCD_TYPE ) < 0 )
479 msg_Err( p_input, "could not read entry points sector" );
484 memcpy( &entries, p_sector, CD_SECTOR_SIZE );
487 if( (i_nb = U16_AT( &entries.i_entries_nb )) > 500 )
489 msg_Err( p_input, "invalid entry points number" );
493 p_sys->p_entries = malloc( sizeof( int ) * i_nb );
494 if( p_sys->p_entries == NULL )
496 msg_Err( p_input, "not enough memory for entry points treatment" );
500 if( strncmp( entries.psz_id, "ENTRYVCD", sizeof( entries.psz_id ) )
501 && strncmp( entries.psz_id, "ENTRYSVD", sizeof( entries.psz_id ) ))
503 msg_Err( p_input, "unrecognized entry points format" );
504 free( p_sys->p_entries );
508 p_sys->i_entries_nb = 0;
510 #define i_track BCD_TO_BIN(entries.entry[i].i_track)
511 /* Reset the i_part_nb for each track */
512 for( i = 0 ; i < i_nb ; i++ )
514 if( i_track <= p_input->stream.i_area_nb )
516 p_input->stream.pp_areas[i_track-1]->i_part_nb = 0;
520 for( i = 0 ; i < i_nb ; i++ )
522 if( i_track <= p_input->stream.i_area_nb )
524 p_sys->p_entries[i_entry_index] =
525 (MSF_TO_LBA2( BCD_TO_BIN( entries.entry[i].msf.minute ),
526 BCD_TO_BIN( entries.entry[i].msf.second ),
527 BCD_TO_BIN( entries.entry[i].msf.frame ) ));
528 p_input->stream.pp_areas[i_track-1]->i_part_nb ++;
529 /* if this entry belongs to a new track */
530 if( i_track != i_previous_track )
532 /* i_plugin_data is used to store the first entry of the area*/
533 p_input->stream.pp_areas[i_track-1]->i_plugin_data =
535 i_previous_track = i_track;
537 msg_Dbg( p_input, "entry point %i begins at LBA: %i",
538 i_entry_index, p_sys->p_entries[i_entry_index] );
541 p_sys->i_entries_nb ++;
544 msg_Warn( p_input, "wrong track number found in entry points" );