]> git.sesse.net Git - vlc/blob - modules/access/vcd/vcd.c
* modules/access/cdda.c, modules/access/vcd/*: New CD digital audio module (by me...
[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.20 2003/05/17 20:30:31 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 "cdrom.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     vcddev_t                *vcddev;
105
106 #ifdef WIN32
107     /* On Win32 we want the VCD access plugin to be explicitly requested,
108      * we end up with lots of problems otherwise */
109     if( !p_input->psz_access || !*p_input->psz_access ) return( -1 );
110 #endif
111
112     /* parse the options passed in command line : */
113     psz_orig = psz_parser = psz_source = strdup( p_input->psz_name );
114
115     if( !psz_orig )
116     {
117         return( -1 );
118     }
119
120     while( *psz_parser && *psz_parser != '@' )
121     {
122         psz_parser++;
123     }
124
125     if( *psz_parser == '@' )
126     {
127         /* Found options */
128         *psz_parser = '\0';
129         ++psz_parser;
130
131         i_title = (int)strtol( psz_parser, &psz_next, 10 );
132         if( *psz_next )
133         {
134             psz_parser = psz_next + 1;
135             i_chapter = (int)strtol( psz_parser, &psz_next, 10 );
136         }
137
138         i_title = i_title ? i_title : 1;
139         i_chapter = i_chapter ? i_chapter : 1;
140     }
141
142     if( !*psz_source )
143     {
144         if( !p_input->psz_access )
145         {
146             free( psz_orig );
147             return -1;
148         }
149         psz_source = config_GetPsz( p_input, "vcd" );
150         if( !psz_source ) return -1;
151     }
152
153     /* Open VCD */
154     if( !(vcddev = ioctl_Open( p_this, psz_source )) )
155     {
156         msg_Warn( p_input, "could not open %s", psz_source );
157         free( psz_source );
158         return -1;
159     }
160
161     p_vcd = malloc( sizeof(thread_vcd_data_t) );
162     if( p_vcd == NULL )
163     {
164         msg_Err( p_input, "out of memory" );
165         free( psz_source );
166         return -1;
167     }
168     free( psz_source );
169
170     p_vcd->vcddev = vcddev;
171     p_input->p_access_data = (void *)p_vcd;
172
173     p_input->i_mtu = VCD_DATA_ONCE;
174
175     vlc_mutex_lock( &p_input->stream.stream_lock );
176     p_input->stream.b_pace_control = 1;
177     p_input->stream.b_seekable = 1;
178     p_input->stream.p_selected_area->i_size = 0;
179     p_input->stream.p_selected_area->i_tell = 0;
180     vlc_mutex_unlock( &p_input->stream.stream_lock );
181
182     /* We read the Table Of Content information */
183     p_vcd->i_nb_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
184                                            p_vcd->vcddev, &p_vcd->p_sectors );
185     if( p_vcd->i_nb_tracks < 0 )
186         msg_Err( p_input, "unable to count tracks" );
187     else if( p_vcd->i_nb_tracks <= 1 )
188         msg_Err( p_input, "no movie tracks found" );
189     if( p_vcd->i_nb_tracks <= 1)
190     {
191         ioctl_Close( p_this, p_vcd->vcddev );
192         free( p_vcd );
193         return -1;
194     }
195
196     /* Allocate the entry points table */
197     p_vcd->p_entries = malloc( p_vcd->i_nb_tracks * sizeof( int ) );
198
199     if( p_vcd->p_entries == NULL )
200     {
201         msg_Err( p_input, "not enough memory" );
202         ioctl_Close( p_this, p_vcd->vcddev );
203         free( p_vcd );
204     }
205
206     /* Set stream and area data */
207     vlc_mutex_lock( &p_input->stream.stream_lock );
208
209     /* Initialize ES structures */
210     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
211
212     /* disc input method */
213     p_input->stream.i_method = INPUT_METHOD_VCD;
214
215     p_input->stream.i_area_nb = 1;
216
217 #define area p_input->stream.pp_areas
218     for( i = 1 ; i <= p_vcd->i_nb_tracks - 1 ; i++ )
219     {
220         /* Titles are Program Chains */
221         input_AddArea( p_input, i, 1 );
222
223         /* Absolute start offset and size */
224         area[i]->i_start = (off_t)p_vcd->p_sectors[i] * (off_t)VCD_DATA_SIZE;
225         area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
226                            * (off_t)VCD_DATA_SIZE;
227
228         /* Default Chapter */
229         area[i]->i_part = 1;
230
231         /* i_plugin_data is used to store which entry point is the first
232          * of the track (area) */
233         area[i]->i_plugin_data = 0;
234     }
235 #undef area
236
237     p_area = p_input->stream.pp_areas[i_title];
238
239     p_vcd->b_valid_ep = 1;
240     if( VCDEntryPoints( p_input ) < 0 )
241     {
242         msg_Warn( p_input, "could not read entry points, will not use them" );
243         p_vcd->b_valid_ep = 0;
244     }
245
246     VCDSetArea( p_input, p_area );
247
248     vlc_mutex_unlock( &p_input->stream.stream_lock );
249
250     if( !p_input->psz_demux || !*p_input->psz_demux )
251     {
252         p_input->psz_demux = "ps";
253     }
254
255     p_input->pf_read = VCDRead;
256     p_input->pf_seek = VCDSeek;
257     p_input->pf_set_area = VCDSetArea;
258     p_input->pf_set_program = VCDSetProgram;
259
260     return 0;
261 }
262
263 /*****************************************************************************
264  * VCDClose: closes vcd
265  *****************************************************************************/
266 static void VCDClose( vlc_object_t *p_this )
267 {
268     input_thread_t *   p_input = (input_thread_t *)p_this;
269     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
270
271     ioctl_Close( p_this, p_vcd->vcddev );
272     free( p_vcd );
273 }
274
275 /*****************************************************************************
276  * VCDRead: reads from the VCD into PES packets.
277  *****************************************************************************
278  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
279  * bytes.
280  *****************************************************************************/
281 static int VCDRead( input_thread_t * p_input, byte_t * p_buffer,
282                      size_t i_len )
283 {
284     thread_vcd_data_t *     p_vcd;
285     int                     i_blocks;
286     int                     i_index;
287     int                     i_read;
288     byte_t                  p_last_sector[ VCD_DATA_SIZE ];
289
290     p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
291
292     i_read = 0;
293
294     /* Compute the number of blocks we have to read */
295
296     i_blocks = i_len / VCD_DATA_SIZE;
297
298     for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
299     {
300         if ( ioctl_ReadSector( VLC_OBJECT(p_input), p_vcd->vcddev,
301              p_vcd->i_sector, p_buffer + i_index * VCD_DATA_SIZE,
302              VCD_DATA_START, 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, VCD_DATA_START,
356              VCD_DATA_SIZE ) < 0 )
357         {
358             msg_Err( p_input, "could not read sector %d", p_vcd->i_sector );
359             return -1;
360         }
361
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;
365     }
366
367     return i_read;
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  * 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 )
384 {
385     thread_vcd_data_t *     p_vcd;
386     vlc_value_t val;
387
388     p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
389
390     /* we can't use the interface slider until initilization is complete */
391     p_input->stream.b_seekable = 0;
392
393     if( p_area != p_input->stream.p_selected_area )
394     {
395         unsigned int i;
396
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;
400
401         /* Change the default area */
402         p_input->stream.p_selected_area = p_area;
403
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];
408
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++ )
414         {
415             val.i_int = i;
416             var_Change( p_input, "chapter", VLC_VAR_ADDCHOICE, &val, NULL );
417         }
418     }
419
420     if( p_vcd->b_valid_ep )
421     {
422         int i_entry = p_area->i_plugin_data /* 1st entry point of
423                                                the track (area)*/
424                             + p_area->i_part - 1;
425         p_vcd->i_sector = p_vcd->p_entries[i_entry];
426     }
427     else
428         p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track];
429
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;
433
434     /* warn interface that something has changed */
435     p_input->stream.b_seekable = 1;
436     p_input->stream.b_changed = 1;
437
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 );
441
442     return 0;
443 }
444
445 /****************************************************************************
446  * VCDSeek
447  ****************************************************************************/
448 static void VCDSeek( input_thread_t * p_input, off_t i_off )
449 {
450     thread_vcd_data_t * p_vcd;
451     unsigned int i_index;
452
453     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
454
455     p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track]
456                        + i_off / (off_t)VCD_DATA_SIZE;
457
458     vlc_mutex_lock( &p_input->stream.stream_lock );
459 #define p_area p_input->stream.p_selected_area
460     /* Find chapter */
461     if( p_vcd->b_valid_ep )
462     {
463         for( i_index = 0 ; i_index < p_area->i_part_nb - 1 ; i_index ++ )
464         {
465             if( p_vcd->i_sector < p_vcd->p_entries[p_area->i_plugin_data
466                 + i_index + 1] )
467             {
468                 p_area->i_part = i_index;
469                 break;
470             }
471         }
472     }
473 #undef p_area
474
475     p_input->stream.p_selected_area->i_tell =
476         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
477          - p_input->stream.p_selected_area->i_start;
478     vlc_mutex_unlock( &p_input->stream.stream_lock );
479 }
480
481 /*****************************************************************************
482  * VCDEntryPoints: Reads the information about the entry points on the disc.
483  *****************************************************************************/
484 static int VCDEntryPoints( input_thread_t * p_input )
485 {
486     thread_vcd_data_t *               p_vcd;
487     byte_t *                          p_sector;
488     entries_sect_t                    entries;
489     uint16_t                          i_nb;
490     int                               i, i_entry_index = 0;
491     int                               i_previous_track = -1;
492
493     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
494
495     p_sector = malloc( VCD_DATA_SIZE * sizeof( byte_t ) );
496     if( p_sector == NULL )
497     {
498         msg_Err( p_input, "not enough memory for entry points treatment" );
499         return -1;
500     }
501
502     if( ioctl_ReadSector( VLC_OBJECT(p_input), p_vcd->vcddev,
503         VCD_ENTRIES_SECTOR, p_sector, VCD_DATA_START, VCD_DATA_SIZE ) < 0 )
504     {
505         msg_Err( p_input, "could not read entry points sector" );
506         free( p_sector );
507         return( -1 );
508     }
509
510     memcpy( &entries, p_sector, CD_SECTOR_SIZE );
511     free( p_sector );
512
513     if( (i_nb = U16_AT( &entries.i_entries_nb )) > 500 )
514     {
515         msg_Err( p_input, "invalid entry points number" );
516         return( -1 );
517     }
518
519     p_vcd->p_entries = malloc( sizeof( int ) * i_nb );
520     if( p_vcd->p_entries == NULL )
521     {
522         msg_Err( p_input, "not enough memory for entry points treatment" );
523         return -1;
524     }
525
526     if( strncmp( entries.psz_id, "ENTRYVCD", sizeof( entries.psz_id ) )
527      && strncmp( entries.psz_id, "ENTRYSVD", sizeof( entries.psz_id ) ))
528     {
529         msg_Err( p_input, "unrecognized entry points format" );
530         free( p_vcd->p_entries );
531         return -1;
532     }
533
534     p_vcd->i_entries_nb = 0;
535
536 #define i_track BCD_TO_BIN(entries.entry[i].i_track)
537     for( i = 0 ; i < i_nb ; i++ )
538     {
539         if( i_track <= p_input->stream.i_area_nb )
540         {
541             p_vcd->p_entries[i_entry_index] =
542                 (MSF_TO_LBA2( BCD_TO_BIN( entries.entry[i].msf.minute ),
543                               BCD_TO_BIN( entries.entry[i].msf.second ),
544                               BCD_TO_BIN( entries.entry[i].msf.frame  ) ));
545             p_input->stream.pp_areas[i_track-1]->i_part_nb ++;
546             /* if this entry belongs to a new track */
547             if( i_track != i_previous_track )
548             {
549                 /* i_plugin_data is used to store the first entry of the area*/
550                 p_input->stream.pp_areas[i_track-1]->i_plugin_data =
551                                                             i_entry_index;
552                 i_previous_track = i_track;
553             }
554             i_entry_index ++;
555             p_vcd->i_entries_nb ++;
556         }
557         else
558             msg_Warn( p_input, "wrong track number found in entry points" );
559     }
560 #undef i_track
561     return 0;
562 }