]> git.sesse.net Git - vlc/blob - modules/access/vcd/vcd.c
* access/*: use var_* helpers.
[vlc] / modules / access / vcd / vcd.c
1 /*****************************************************************************
2  * vcd.c : VCD input module for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2004 VideoLAN
5  * $Id$
6  *
7  * Author: Johan Bilien <jobi@via.ecp.fr>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28
29 #include <vlc/vlc.h>
30 #include <vlc/input.h>
31
32 #include "cdrom.h"
33
34 /*****************************************************************************
35  * Module descriptior
36  *****************************************************************************/
37 static int  Open ( vlc_object_t * );
38 static void Close( vlc_object_t * );
39
40 vlc_module_begin();
41     set_description( _("VCD input") );
42     set_capability( "access", 80 );
43     set_callbacks( Open, Close );
44
45     add_usage_hint( N_("[vcd:][device][@[title][,[chapter]]]") );
46     add_shortcut( "svcd" );
47 vlc_module_end();
48
49 /*****************************************************************************
50  * Local prototypes
51  *****************************************************************************/
52
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)
56
57 struct access_sys_t
58 {
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 */
68 };
69
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 * );
75
76 /*****************************************************************************
77  * VCDOpen: open vcd
78  *****************************************************************************/
79 static int Open( vlc_object_t *p_this )
80 {
81     input_thread_t *p_input = (input_thread_t *)p_this;
82     access_sys_t   *p_sys;
83     char *psz_dup = strdup( p_input->psz_name );
84     char *psz;
85
86     int                     i;
87     input_area_t *          p_area;
88     int                     i_title = 0;
89     int                     i_chapter = 0;
90     vcddev_t                *vcddev;
91
92     /* Command line: vcd://[dev_path][@title[,chapter]] */
93     if( ( psz = strchr( psz_dup, '@' ) ) )
94     {
95         *psz++ = '\0';
96
97         i_title = strtol( psz, &psz, 0 );
98         if( *psz )
99         {
100             i_chapter = strtol( psz, &psz, 0 );
101         }
102     }
103
104     if( *psz_dup == '\0' )
105     {
106         free( psz_dup );
107
108         /* Only when selected */
109         if( *p_input->psz_access == '\0' )
110             return VLC_EGENERIC;
111
112         psz_dup = var_CreateGetString( p_input, "vcd" );
113         if( *psz_dup == '\0' )
114         {
115             free( psz_dup );
116             return VLC_EGENERIC;
117         }
118     }
119
120     /* Open VCD */
121     if( !(vcddev = ioctl_Open( p_this, psz_dup )) )
122     {
123         msg_Warn( p_input, "could not open %s", psz_dup );
124         free( psz_dup );
125         return VLC_EGENERIC;
126     }
127
128     p_sys = malloc( sizeof(access_sys_t) );
129     if( p_sys == NULL )
130     {
131         msg_Err( p_input, "out of memory" );
132         free( psz_dup );
133         return VLC_EGENERIC;
134     }
135     free( psz_dup );
136
137     p_sys->vcddev = vcddev;
138     p_input->p_access_data = (void *)p_sys;
139
140     p_input->i_mtu = VCD_DATA_ONCE;
141
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 );
148
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)
157     {
158         ioctl_Close( p_this, p_sys->vcddev );
159         free( p_sys );
160         return -1;
161     }
162
163     /* Allocate the entry points table */
164     p_sys->p_entries = malloc( p_sys->i_nb_tracks * sizeof( int ) );
165
166     if( p_sys->p_entries == NULL )
167     {
168         msg_Err( p_input, "not enough memory" );
169         ioctl_Close( p_this, p_sys->vcddev );
170         free( p_sys );
171     }
172
173     /* Set stream and area data */
174     vlc_mutex_lock( &p_input->stream.stream_lock );
175
176     /* Initialize ES structures */
177     input_InitStream( p_input, 0 );
178
179     /* disc input method */
180     p_input->stream.i_method = INPUT_METHOD_VCD;
181
182     p_input->stream.i_area_nb = 1;
183
184 #define area p_input->stream.pp_areas
185     for( i = 1 ; i < p_sys->i_nb_tracks; i++ )
186     {
187         /* Titles are Program Chains */
188         input_AddArea( p_input, i, 1 );
189
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;
194
195         /* Default Chapter */
196         area[i]->i_part = 1;
197
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;
201     }
202 #undef area
203
204     p_area = p_input->stream.pp_areas[__MIN(i_title,p_sys->i_nb_tracks -1)];
205
206     p_sys->b_valid_ep = 1;
207     if( VCDEntryPoints( p_input ) < 0 )
208     {
209         msg_Warn( p_input, "could not read entry points, will not use them" );
210         p_sys->b_valid_ep = 0;
211     }
212
213     VCDSetArea( p_input, p_area );
214
215     vlc_mutex_unlock( &p_input->stream.stream_lock );
216
217     if( !p_input->psz_demux || !*p_input->psz_demux )
218     {
219         p_input->psz_demux = "ps";
220     }
221
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;
226
227     return 0;
228 }
229
230 /*****************************************************************************
231  * VCDClose: closes vcd
232  *****************************************************************************/
233 static void Close( vlc_object_t *p_this )
234 {
235     input_thread_t *   p_input = (input_thread_t *)p_this;
236     access_sys_t *p_sys = p_input->p_access_data;
237
238     ioctl_Close( p_this, p_sys->vcddev );
239     free( p_sys );
240 }
241
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
246  * bytes.
247  *****************************************************************************/
248 static int VCDRead( input_thread_t * p_input, byte_t * p_buffer,
249                      size_t i_len )
250 {
251     access_sys_t *p_sys;
252     int                     i_blocks;
253     int                     i_index;
254     int                     i_read;
255     byte_t                  p_last_sector[ VCD_DATA_SIZE ];
256
257     p_sys = p_input->p_access_data;
258
259     i_read = 0;
260
261     /* Compute the number of blocks we have to read */
262
263     i_blocks = i_len / VCD_DATA_SIZE;
264
265     for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
266     {
267         if ( ioctl_ReadSectors( VLC_OBJECT(p_input), p_sys->vcddev,
268              p_sys->i_sector, p_buffer + i_index * VCD_DATA_SIZE, 1,
269              VCD_TYPE ) < 0 )
270         {
271             msg_Err( p_input, "could not read sector %d", p_sys->i_sector );
272             return -1;
273         }
274
275         p_sys->i_sector ++;
276         if ( p_sys->i_sector == p_sys->p_sectors[p_sys->i_track + 1] )
277         {
278             input_area_t *p_area;
279
280             if ( p_sys->i_track >= p_sys->i_nb_tracks - 1 )
281                 return 0; /* EOF */
282
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 ];
286
287             msg_Dbg( p_input, "new title" );
288
289             p_area->i_part = 1;
290             VCDSetArea( p_input, p_area );
291             vlc_mutex_unlock( &p_input->stream.stream_lock );
292         }
293
294         /* Update chapter */
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 )
300         {
301             int i_entry;
302
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] )
309             {
310                 vlc_value_t val;
311
312                 msg_Dbg( p_input, "new chapter" );
313                 p_input->stream.p_selected_area->i_part ++;
314
315                 /* Update the navigation variables without triggering
316                  * a callback */
317                 val.i_int = p_input->stream.p_selected_area->i_part;
318                 var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val, NULL );
319             }
320             vlc_mutex_unlock( &p_input->stream.stream_lock );
321         }
322
323         i_read += VCD_DATA_SIZE;
324     }
325
326     if ( i_len % VCD_DATA_SIZE ) /* this should not happen */
327     {
328         if ( ioctl_ReadSectors( VLC_OBJECT(p_input), p_sys->vcddev,
329              p_sys->i_sector, p_last_sector, 1, VCD_TYPE ) < 0 )
330         {
331             msg_Err( p_input, "could not read sector %d", p_sys->i_sector );
332             return -1;
333         }
334
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;
338     }
339
340     return i_read;
341 }
342
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)
348 {
349     return 0;
350 }
351
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 )
357 {
358     access_sys_t *p_sys = p_input->p_access_data;
359     vlc_value_t val;
360
361     /* we can't use the interface slider until initilization is complete */
362     p_input->stream.b_seekable = 0;
363
364     if( p_area != p_input->stream.p_selected_area )
365     {
366         unsigned int i;
367
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;
371
372         /* Change the default area */
373         p_input->stream.p_selected_area = p_area;
374
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];
379
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++ )
385         {
386             val.i_int = i;
387             var_Change( p_input, "chapter", VLC_VAR_ADDCHOICE, &val, NULL );
388         }
389     }
390
391     if( p_sys->b_valid_ep )
392     {
393         int i_entry = p_area->i_plugin_data /* 1st entry point of
394                                                the track (area)*/
395                             + p_area->i_part - 1;
396         p_sys->i_sector = p_sys->p_entries[i_entry];
397     }
398     else
399         p_sys->i_sector = p_sys->p_sectors[p_sys->i_track];
400
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;
404
405     /* warn interface that something has changed */
406     p_input->stream.b_seekable = 1;
407     p_input->stream.b_changed = 1;
408
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 );
412
413     return 0;
414 }
415
416 /****************************************************************************
417  * VCDSeek
418  ****************************************************************************/
419 static void VCDSeek( input_thread_t * p_input, off_t i_off )
420 {
421     access_sys_t * p_sys = p_input->p_access_data;
422     unsigned int i_index;
423
424     p_sys->i_sector = p_sys->p_sectors[p_sys->i_track]
425                        + i_off / (off_t)VCD_DATA_SIZE;
426
427     vlc_mutex_lock( &p_input->stream.stream_lock );
428 #define p_area p_input->stream.p_selected_area
429     /* Find chapter */
430     if( p_sys->b_valid_ep )
431     {
432         for( i_index = 2 ; i_index <= p_area->i_part_nb; i_index ++ )
433         {
434             if( p_sys->i_sector < p_sys->p_entries[p_area->i_plugin_data
435                 + i_index - 1] )
436             {
437                 vlc_value_t val;
438
439                 p_area->i_part = i_index - 1;
440
441                 /* Update the navigation variables without triggering
442                  * a callback */
443                 val.i_int = p_area->i_part;
444                 var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val, NULL );
445                 break;
446             }
447         }
448     }
449 #undef p_area
450
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 );
455 }
456
457 /*****************************************************************************
458  * VCDEntryPoints: Reads the information about the entry points on the disc.
459  *****************************************************************************/
460 static int VCDEntryPoints( input_thread_t * p_input )
461 {
462     access_sys_t * p_sys = p_input->p_access_data;
463     byte_t *                          p_sector;
464     entries_sect_t                    entries;
465     uint16_t                          i_nb;
466     int                               i, i_entry_index = 0;
467     int                               i_previous_track = -1;
468
469     p_sector = malloc( VCD_DATA_SIZE * sizeof( byte_t ) );
470     if( p_sector == NULL )
471     {
472         msg_Err( p_input, "not enough memory for entry points treatment" );
473         return -1;
474     }
475
476     if( ioctl_ReadSectors( VLC_OBJECT(p_input), p_sys->vcddev,
477         VCD_ENTRIES_SECTOR, p_sector, 1, VCD_TYPE ) < 0 )
478     {
479         msg_Err( p_input, "could not read entry points sector" );
480         free( p_sector );
481         return( -1 );
482     }
483
484     memcpy( &entries, p_sector, CD_SECTOR_SIZE );
485     free( p_sector );
486
487     if( (i_nb = U16_AT( &entries.i_entries_nb )) > 500 )
488     {
489         msg_Err( p_input, "invalid entry points number" );
490         return( -1 );
491     }
492
493     p_sys->p_entries = malloc( sizeof( int ) * i_nb );
494     if( p_sys->p_entries == NULL )
495     {
496         msg_Err( p_input, "not enough memory for entry points treatment" );
497         return -1;
498     }
499
500     if( strncmp( entries.psz_id, "ENTRYVCD", sizeof( entries.psz_id ) )
501      && strncmp( entries.psz_id, "ENTRYSVD", sizeof( entries.psz_id ) ))
502     {
503         msg_Err( p_input, "unrecognized entry points format" );
504         free( p_sys->p_entries );
505         return -1;
506     }
507
508     p_sys->i_entries_nb = 0;
509
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++ )
513     {
514         if( i_track <= p_input->stream.i_area_nb )
515         {
516             p_input->stream.pp_areas[i_track-1]->i_part_nb = 0;
517         }
518     }
519
520     for( i = 0 ; i < i_nb ; i++ )
521     {
522         if( i_track <= p_input->stream.i_area_nb )
523         {
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 )
531             {
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 =
534                                                             i_entry_index;
535                 i_previous_track = i_track;
536             }
537             msg_Dbg( p_input, "entry point %i begins at LBA: %i",
538                      i_entry_index, p_sys->p_entries[i_entry_index] );
539
540             i_entry_index ++;
541             p_sys->i_entries_nb ++;
542         }
543         else
544             msg_Warn( p_input, "wrong track number found in entry points" );
545     }
546 #undef i_track
547     return 0;
548 }