]> git.sesse.net Git - vlc/blob - modules/access/vcd/vcd.c
e092ddb5c455fb7c918241a4f401b617e73f4c27
[vlc] / modules / access / vcd / vcd.c
1 /*****************************************************************************
2  * vcd.c : VCD input module for vlc
3  *****************************************************************************
4  * Copyright (C) 2000 VideoLAN
5  * $Id: vcd.c,v 1.18 2003/03/30 18:14:36 gbazin Exp $
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 <stdio.h>
28 #include <stdlib.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc/input.h>
32
33 #include "../../demux/mpeg/system.h"
34
35 #ifdef HAVE_UNISTD_H
36 #   include <unistd.h>
37 #endif
38
39 #include <string.h>
40
41 #include "vcd.h"
42
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)
46
47 /*****************************************************************************
48  * thread_vcd_data_t: VCD information
49  *****************************************************************************/
50 typedef struct thread_vcd_data_s
51 {
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 */
61
62 } thread_vcd_data_t;
63
64 /*****************************************************************************
65  * Local prototypes
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 * );
74
75 /*****************************************************************************
76  * Module descriptior
77  *****************************************************************************/
78 vlc_module_begin();
79     set_description( _("VCD input") );
80     set_capability( "access", 80 );
81     set_callbacks( VCDOpen, VCDClose );
82     add_shortcut( "svcd" );
83 vlc_module_end();
84
85 /*
86  * Data reading functions
87  */
88
89 /*****************************************************************************
90  * VCDOpen: open vcd
91  *****************************************************************************/
92 static int VCDOpen( vlc_object_t *p_this )
93 {
94     input_thread_t *        p_input = (input_thread_t *)p_this;
95     char *                  psz_orig;
96     char *                  psz_parser;
97     char *                  psz_source;
98     char *                  psz_next;
99     thread_vcd_data_t *     p_vcd;
100     int                     i;
101     input_area_t *          p_area;
102     int                     i_title = 1;
103     int                     i_chapter = 1;
104
105     p_input->pf_read = VCDRead;
106     p_input->pf_seek = VCDSeek;
107     p_input->pf_set_area = VCDSetArea;
108     p_input->pf_set_program = VCDSetProgram;
109
110 #ifdef WIN32
111     /* On Win32 we want the VCD access plugin to be explicitly requested,
112      * we end up with lots of problems otherwise */
113     if( !p_input->psz_access || !*p_input->psz_access ) return( -1 );
114 #endif
115
116     /* parse the options passed in command line : */
117     psz_orig = psz_parser = psz_source = strdup( p_input->psz_name );
118
119     if( !psz_orig )
120     {
121         return( -1 );
122     }
123
124     while( *psz_parser && *psz_parser != '@' )
125     {
126         psz_parser++;
127     }
128
129     if( *psz_parser == '@' )
130     {
131         /* Found options */
132         *psz_parser = '\0';
133         ++psz_parser;
134
135         i_title = (int)strtol( psz_parser, &psz_next, 10 );
136         if( *psz_next )
137         {
138             psz_parser = psz_next + 1;
139             i_chapter = (int)strtol( psz_parser, &psz_next, 10 );
140         }
141
142         i_title = i_title ? i_title : 1;
143         i_chapter = i_chapter ? i_chapter : 1;
144     }
145
146     if( !*psz_source )
147     {
148         if( !p_input->psz_access )
149         {
150             free( psz_orig );
151             return -1;
152         }
153         psz_source = config_GetPsz( p_input, "vcd" );
154         if( !psz_source ) return -1;
155     }
156
157     p_vcd = malloc( sizeof(thread_vcd_data_t) );
158
159     if( p_vcd == NULL )
160     {
161         msg_Err( p_input, "out of memory" );
162         free( psz_source );
163         return -1;
164     }
165
166     p_input->p_access_data = (void *)p_vcd;
167
168     p_input->i_mtu = VCD_DATA_ONCE;
169
170     vlc_mutex_lock( &p_input->stream.stream_lock );
171
172     p_input->stream.b_pace_control = 1;
173     p_input->stream.b_seekable = 1;
174     p_input->stream.p_selected_area->i_size = 0;
175     p_input->stream.p_selected_area->i_tell = 0;
176
177     vlc_mutex_unlock( &p_input->stream.stream_lock );
178
179     if( !(p_vcd->vcddev = ioctl_Open( p_this, psz_source )) )
180     {
181         msg_Warn( p_input, "could not open %s", psz_source );
182         free( psz_source );
183         free( p_vcd );
184         return -1;
185     }
186
187     /* We read the Table Of Content information */
188     p_vcd->i_nb_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
189                                            p_vcd->vcddev, &p_vcd->p_sectors );
190     free( psz_source );
191     if( p_vcd->i_nb_tracks < 0 )
192         msg_Err( p_input, "unable to count tracks" );
193     else if( p_vcd->i_nb_tracks <= 1 )
194         msg_Err( p_input, "no movie tracks found" );
195     if( p_vcd->i_nb_tracks <= 1)
196     {
197         ioctl_Close( p_this, p_vcd->vcddev );
198         free( p_vcd );
199         return -1;
200     }
201
202     /* Allocate the entry points table */
203     p_vcd->p_entries = malloc( p_vcd->i_nb_tracks * sizeof( int ) );
204
205     if( p_vcd->p_entries == NULL )
206     {
207         msg_Err( p_input, "not enough memory" );
208         ioctl_Close( p_this, p_vcd->vcddev );
209         free( p_vcd );
210     }
211
212     /* Set stream and area data */
213     vlc_mutex_lock( &p_input->stream.stream_lock );
214
215     /* Initialize ES structures */
216     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
217
218     /* disc input method */
219     p_input->stream.i_method = INPUT_METHOD_VCD;
220
221     p_input->stream.i_area_nb = 1;
222
223 #define area p_input->stream.pp_areas
224     for( i = 1 ; i <= p_vcd->i_nb_tracks - 1 ; i++ )
225     {
226         /* Titles are Program Chains */
227         input_AddArea( p_input, i, 1 );
228
229         /* Absolute start offset and size */
230         area[i]->i_start = (off_t)p_vcd->p_sectors[i] * (off_t)VCD_DATA_SIZE;
231         area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
232                            * (off_t)VCD_DATA_SIZE;
233
234         /* Default Chapter */
235         area[i]->i_part = 1;
236
237         /* i_plugin_data is used to store which entry point is the first
238          * of the track (area) */
239         area[i]->i_plugin_data = 0;
240     }
241 #undef area
242
243     p_area = p_input->stream.pp_areas[i_title];
244
245     p_vcd->b_valid_ep = 1;
246     if( VCDEntryPoints( p_input ) < 0 )
247     {
248         msg_Warn( p_input, "could not read entry points, will not use them" );
249         p_vcd->b_valid_ep = 0;
250     }
251
252     VCDSetArea( p_input, p_area );
253
254     vlc_mutex_unlock( &p_input->stream.stream_lock );
255
256     if( !p_input->psz_demux || !*p_input->psz_demux )
257     {
258         p_input->psz_demux = "ps";
259     }
260
261     return 0;
262 }
263
264 /*****************************************************************************
265  * VCDClose: closes vcd
266  *****************************************************************************/
267 static void VCDClose( vlc_object_t *p_this )
268 {
269     input_thread_t *   p_input = (input_thread_t *)p_this;
270     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
271
272     ioctl_Close( p_this, p_vcd->vcddev );
273     free( p_vcd );
274 }
275
276 /*****************************************************************************
277  * VCDRead: reads from the VCD into PES packets.
278  *****************************************************************************
279  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
280  * bytes.
281  *****************************************************************************/
282 static int VCDRead( input_thread_t * p_input, byte_t * p_buffer,
283                      size_t i_len )
284 {
285     thread_vcd_data_t *     p_vcd;
286     int                     i_blocks;
287     int                     i_index;
288     int                     i_read;
289     byte_t                  p_last_sector[ VCD_DATA_SIZE ];
290
291     p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
292
293     i_read = 0;
294
295     /* Compute the number of blocks we have to read */
296
297     i_blocks = i_len / VCD_DATA_SIZE;
298
299     for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
300     {
301         if ( ioctl_ReadSector( VLC_OBJECT(p_input), p_vcd->vcddev,
302                     p_vcd->i_sector, p_buffer + i_index * VCD_DATA_SIZE ) < 0 )
303         {
304             msg_Err( p_input, "could not read sector %d", p_vcd->i_sector );
305             return -1;
306         }
307
308         p_vcd->i_sector ++;
309         if ( p_vcd->i_sector == p_vcd->p_sectors[p_vcd->i_track + 1] )
310         {
311             input_area_t *p_area;
312
313             if ( p_vcd->i_track >= p_vcd->i_nb_tracks - 1 )
314                 return 0; /* EOF */
315
316             vlc_mutex_lock( &p_input->stream.stream_lock );
317             p_area = p_input->stream.pp_areas[
318                     p_input->stream.p_selected_area->i_id + 1 ];
319
320             msg_Dbg( p_input, "new title" );
321
322             p_area->i_part = 1;
323             VCDSetArea( p_input, p_area );
324             vlc_mutex_unlock( &p_input->stream.stream_lock );
325         }
326
327         /* Update chapter */
328         else if( p_vcd->b_valid_ep &&
329                 /* FIXME kludge so that read does not update chapter
330                  * when a manual chapter change was requested and not
331                  * yet accomplished */
332                 !p_input->stream.p_new_area )
333         {
334             int i_entry;
335
336             vlc_mutex_lock( &p_input->stream.stream_lock );
337             i_entry = p_input->stream.p_selected_area->i_plugin_data
338                 /* 1st entry point of the track (area)*/
339                         + p_input->stream.p_selected_area->i_part - 1;
340             if( i_entry + 1 < p_vcd->i_entries_nb &&
341                     p_vcd->i_sector >= p_vcd->p_entries[i_entry + 1] )
342             {
343                 msg_Dbg( p_input, "new chapter" );
344                 p_input->stream.p_selected_area->i_part ++;
345             }
346             vlc_mutex_unlock( &p_input->stream.stream_lock );
347         }
348
349         i_read += VCD_DATA_SIZE;
350     }
351
352     if ( i_len % VCD_DATA_SIZE ) /* this should not happen */
353     {
354         if ( ioctl_ReadSector( VLC_OBJECT(p_input), p_vcd->vcddev,
355                                p_vcd->i_sector, p_last_sector ) < 0 )
356         {
357             msg_Err( p_input, "could not read sector %d", p_vcd->i_sector );
358             return -1;
359         }
360
361         p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * VCD_DATA_SIZE,
362                                    p_last_sector, i_len % VCD_DATA_SIZE );
363         i_read += i_len % VCD_DATA_SIZE;
364     }
365
366     return i_read;
367 }
368
369
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)
375 {
376     return 0;
377 }
378
379
380 /*****************************************************************************
381  * VCDSetArea: initialize input data for title x, chapter y.
382  * It should be called for each user navigation request.
383  ****************************************************************************/
384 static int VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
385 {
386     thread_vcd_data_t *     p_vcd;
387     vlc_value_t val;
388
389     p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
390
391     /* we can't use the interface slider until initilization is complete */
392     p_input->stream.b_seekable = 0;
393
394     if( p_area != p_input->stream.p_selected_area )
395     {
396         unsigned int i;
397
398         /* Reset the Chapter position of the current title */
399         p_input->stream.p_selected_area->i_part = 1;
400         p_input->stream.p_selected_area->i_tell = 0;
401
402         /* Change the default area */
403         p_input->stream.p_selected_area = p_area;
404
405         /* Change the current track */
406         /* The first track is not a valid one  */
407         p_vcd->i_track = p_area->i_id;
408         p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track];
409
410         /* Update the navigation variables without triggering a callback */
411         val.i_int = p_area->i_id;
412         var_Change( p_input, "title", VLC_VAR_SETVALUE, &val );
413         var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL );
414         for( i = 1; i <= p_area->i_part_nb; i++ )
415         {
416             val.i_int = i;
417             var_Change( p_input, "chapter", VLC_VAR_ADDCHOICE, &val );
418         }
419     }
420
421     if( p_vcd->b_valid_ep )
422     {
423         int i_entry = p_area->i_plugin_data /* 1st entry point of
424                                                the track (area)*/
425                             + p_area->i_part - 1;
426         p_vcd->i_sector = p_vcd->p_entries[i_entry];
427     }
428     else
429         p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track];
430
431     p_input->stream.p_selected_area->i_tell =
432         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
433          - p_input->stream.p_selected_area->i_start;
434
435     /* warn interface that something has changed */
436     p_input->stream.b_seekable = 1;
437     p_input->stream.b_changed = 1;
438
439     /* Update the navigation variables without triggering a callback */
440     val.i_int = p_area->i_part;
441     var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val );
442
443     return 0;
444 }
445
446
447 /****************************************************************************
448  * VCDSeek
449  ****************************************************************************/
450 static void VCDSeek( input_thread_t * p_input, off_t i_off )
451 {
452     thread_vcd_data_t * p_vcd;
453     unsigned int i_index;
454
455     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
456
457     p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track]
458                        + i_off / (off_t)VCD_DATA_SIZE;
459
460     vlc_mutex_lock( &p_input->stream.stream_lock );
461 #define p_area p_input->stream.p_selected_area
462     /* Find chapter */
463     if( p_vcd->b_valid_ep )
464     {
465         for( i_index = 0 ; i_index < p_area->i_part_nb - 1 ; i_index ++ )
466         {
467             if( p_vcd->i_sector < p_vcd->p_entries[p_area->i_plugin_data
468                 + i_index + 1] )
469             {
470                 p_area->i_part = i_index;
471                 break;
472             }
473         }
474     }
475 #undef p_area
476
477     p_input->stream.p_selected_area->i_tell =
478         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
479          - p_input->stream.p_selected_area->i_start;
480     vlc_mutex_unlock( &p_input->stream.stream_lock );
481 }
482
483 /*****************************************************************************
484  * VCDEntryPoints: Reads the information about the entry points on the disc.
485  *****************************************************************************/
486 static int VCDEntryPoints( input_thread_t * p_input )
487 {
488     thread_vcd_data_t *               p_vcd;
489     byte_t *                          p_sector;
490     entries_sect_t                    entries;
491     uint16_t                          i_nb;
492     int                               i, i_entry_index = 0;
493     int                               i_previous_track = -1;
494
495     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
496
497     p_sector = malloc( VCD_DATA_SIZE * sizeof( byte_t ) );
498     if( p_sector == NULL )
499     {
500         msg_Err( p_input, "not enough memory for entry points treatment" );
501         return -1;
502     }
503
504     if( ioctl_ReadSector( VLC_OBJECT(p_input), p_vcd->vcddev,
505                                 VCD_ENTRIES_SECTOR, p_sector ) < 0 )
506     {
507         msg_Err( p_input, "could not read entry points sector" );
508         free( p_sector );
509         return( -1 );
510     }
511
512     memcpy( &entries, p_sector, CD_SECTOR_SIZE );
513     free( p_sector );
514
515     if( (i_nb = U16_AT( &entries.i_entries_nb )) > 500 )
516     {
517         msg_Err( p_input, "invalid entry points number" );
518         return( -1 );
519     }
520
521     p_vcd->p_entries = malloc( sizeof( int ) * i_nb );
522     if( p_vcd->p_entries == NULL )
523     {
524         msg_Err( p_input, "not enough memory for entry points treatment" );
525         return -1;
526     }
527
528     if( strncmp( entries.psz_id, "ENTRYVCD", sizeof( entries.psz_id ) )
529      && strncmp( entries.psz_id, "ENTRYSVD", sizeof( entries.psz_id ) ))
530     {
531         msg_Err( p_input, "unrecognized entry points format" );
532         free( p_vcd->p_entries );
533         return -1;
534     }
535
536     p_vcd->i_entries_nb = 0;
537
538 #define i_track BCD_TO_BIN(entries.entry[i].i_track)
539     for( i = 0 ; i < i_nb ; i++ )
540     {
541         if( i_track <= p_input->stream.i_area_nb )
542         {
543             p_vcd->p_entries[i_entry_index] =
544                 (MSF_TO_LBA2( BCD_TO_BIN( entries.entry[i].msf.minute ),
545                               BCD_TO_BIN( entries.entry[i].msf.second ),
546                               BCD_TO_BIN( entries.entry[i].msf.frame  ) ));
547             p_input->stream.pp_areas[i_track-1]->i_part_nb ++;
548             /* if this entry belongs to a new track */
549             if( i_track != i_previous_track )
550             {
551                 /* i_plugin_data is used to store the first entry of the area*/
552                 p_input->stream.pp_areas[i_track-1]->i_plugin_data =
553                                                             i_entry_index;
554                 i_previous_track = i_track;
555             }
556             i_entry_index ++;
557             p_vcd->i_entries_nb ++;
558         }
559         else
560             msg_Warn( p_input, "wrong track number found in entry points" );
561     }
562 #undef i_track
563     return 0;
564 }