]> git.sesse.net Git - vlc/blob - modules/access/vcd/vcd.c
0a10eb70b7732bc9388d7b9a09c5f86a9c82be41
[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.21 2003/05/18 15:44:03 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_ReadSectors( VLC_OBJECT(p_input), p_vcd->vcddev,
301              p_vcd->i_sector, p_buffer + i_index * VCD_DATA_SIZE, 1,
302              VCD_TYPE ) < 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_ReadSectors( VLC_OBJECT(p_input), p_vcd->vcddev,
355              p_vcd->i_sector, p_last_sector, 1, VCD_TYPE ) < 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  * VCDSetProgram: Does nothing since a VCD is mono_program
371  *****************************************************************************/
372 static int VCDSetProgram( input_thread_t * p_input,
373                           pgrm_descriptor_t * p_program)
374 {
375     return 0;
376 }
377
378 /*****************************************************************************
379  * VCDSetArea: initialize input data for title x, chapter y.
380  * It should be called for each user navigation request.
381  ****************************************************************************/
382 static int VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
383 {
384     thread_vcd_data_t *     p_vcd;
385     vlc_value_t val;
386
387     p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
388
389     /* we can't use the interface slider until initilization is complete */
390     p_input->stream.b_seekable = 0;
391
392     if( p_area != p_input->stream.p_selected_area )
393     {
394         unsigned int i;
395
396         /* Reset the Chapter position of the current title */
397         p_input->stream.p_selected_area->i_part = 1;
398         p_input->stream.p_selected_area->i_tell = 0;
399
400         /* Change the default area */
401         p_input->stream.p_selected_area = p_area;
402
403         /* Change the current track */
404         /* The first track is not a valid one  */
405         p_vcd->i_track = p_area->i_id;
406         p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track];
407
408         /* Update the navigation variables without triggering a callback */
409         val.i_int = p_area->i_id;
410         var_Change( p_input, "title", VLC_VAR_SETVALUE, &val, NULL );
411         var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
412         for( i = 1; i <= p_area->i_part_nb; i++ )
413         {
414             val.i_int = i;
415             var_Change( p_input, "chapter", VLC_VAR_ADDCHOICE, &val, NULL );
416         }
417     }
418
419     if( p_vcd->b_valid_ep )
420     {
421         int i_entry = p_area->i_plugin_data /* 1st entry point of
422                                                the track (area)*/
423                             + p_area->i_part - 1;
424         p_vcd->i_sector = p_vcd->p_entries[i_entry];
425     }
426     else
427         p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track];
428
429     p_input->stream.p_selected_area->i_tell =
430         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
431          - p_input->stream.p_selected_area->i_start;
432
433     /* warn interface that something has changed */
434     p_input->stream.b_seekable = 1;
435     p_input->stream.b_changed = 1;
436
437     /* Update the navigation variables without triggering a callback */
438     val.i_int = p_area->i_part;
439     var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val, NULL );
440
441     return 0;
442 }
443
444 /****************************************************************************
445  * VCDSeek
446  ****************************************************************************/
447 static void VCDSeek( input_thread_t * p_input, off_t i_off )
448 {
449     thread_vcd_data_t * p_vcd;
450     unsigned int i_index;
451
452     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
453
454     p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track]
455                        + i_off / (off_t)VCD_DATA_SIZE;
456
457     vlc_mutex_lock( &p_input->stream.stream_lock );
458 #define p_area p_input->stream.p_selected_area
459     /* Find chapter */
460     if( p_vcd->b_valid_ep )
461     {
462         for( i_index = 0 ; i_index < p_area->i_part_nb - 1 ; i_index ++ )
463         {
464             if( p_vcd->i_sector < p_vcd->p_entries[p_area->i_plugin_data
465                 + i_index + 1] )
466             {
467                 p_area->i_part = i_index;
468                 break;
469             }
470         }
471     }
472 #undef p_area
473
474     p_input->stream.p_selected_area->i_tell =
475         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
476          - p_input->stream.p_selected_area->i_start;
477     vlc_mutex_unlock( &p_input->stream.stream_lock );
478 }
479
480 /*****************************************************************************
481  * VCDEntryPoints: Reads the information about the entry points on the disc.
482  *****************************************************************************/
483 static int VCDEntryPoints( input_thread_t * p_input )
484 {
485     thread_vcd_data_t *               p_vcd;
486     byte_t *                          p_sector;
487     entries_sect_t                    entries;
488     uint16_t                          i_nb;
489     int                               i, i_entry_index = 0;
490     int                               i_previous_track = -1;
491
492     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
493
494     p_sector = malloc( VCD_DATA_SIZE * sizeof( byte_t ) );
495     if( p_sector == NULL )
496     {
497         msg_Err( p_input, "not enough memory for entry points treatment" );
498         return -1;
499     }
500
501     if( ioctl_ReadSectors( VLC_OBJECT(p_input), p_vcd->vcddev,
502         VCD_ENTRIES_SECTOR, p_sector, 1, VCD_TYPE ) < 0 )
503     {
504         msg_Err( p_input, "could not read entry points sector" );
505         free( p_sector );
506         return( -1 );
507     }
508
509     memcpy( &entries, p_sector, CD_SECTOR_SIZE );
510     free( p_sector );
511
512     if( (i_nb = U16_AT( &entries.i_entries_nb )) > 500 )
513     {
514         msg_Err( p_input, "invalid entry points number" );
515         return( -1 );
516     }
517
518     p_vcd->p_entries = malloc( sizeof( int ) * i_nb );
519     if( p_vcd->p_entries == NULL )
520     {
521         msg_Err( p_input, "not enough memory for entry points treatment" );
522         return -1;
523     }
524
525     if( strncmp( entries.psz_id, "ENTRYVCD", sizeof( entries.psz_id ) )
526      && strncmp( entries.psz_id, "ENTRYSVD", sizeof( entries.psz_id ) ))
527     {
528         msg_Err( p_input, "unrecognized entry points format" );
529         free( p_vcd->p_entries );
530         return -1;
531     }
532
533     p_vcd->i_entries_nb = 0;
534
535 #define i_track BCD_TO_BIN(entries.entry[i].i_track)
536     for( i = 0 ; i < i_nb ; i++ )
537     {
538         if( i_track <= p_input->stream.i_area_nb )
539         {
540             p_vcd->p_entries[i_entry_index] =
541                 (MSF_TO_LBA2( BCD_TO_BIN( entries.entry[i].msf.minute ),
542                               BCD_TO_BIN( entries.entry[i].msf.second ),
543                               BCD_TO_BIN( entries.entry[i].msf.frame  ) ));
544             p_input->stream.pp_areas[i_track-1]->i_part_nb ++;
545             /* if this entry belongs to a new track */
546             if( i_track != i_previous_track )
547             {
548                 /* i_plugin_data is used to store the first entry of the area*/
549                 p_input->stream.pp_areas[i_track-1]->i_plugin_data =
550                                                             i_entry_index;
551                 i_previous_track = i_track;
552             }
553             i_entry_index ++;
554             p_vcd->i_entries_nb ++;
555         }
556         else
557             msg_Warn( p_input, "wrong track number found in entry points" );
558     }
559 #undef i_track
560     return 0;
561 }