]> git.sesse.net Git - vlc/blob - modules/access/vcd/vcd.c
* ./configure.ac.in: removed -W in favour of -Wtraditional.
[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.12 2002/12/06 16:34:04 sam 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 module") );
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
174     p_input->stream.b_seekable = 1;
175     p_input->stream.p_selected_area->i_size = 0;
176     p_input->stream.p_selected_area->i_tell = 0;
177
178     vlc_mutex_unlock( &p_input->stream.stream_lock );
179
180     if( !(p_vcd->vcddev = ioctl_Open( p_this, psz_source )) )
181     {
182         msg_Warn( p_input, "could not open %s", psz_source );
183         free( psz_source );
184         free( p_vcd );
185         return -1;
186     }
187
188     /* We read the Table Of Content information */
189     p_vcd->i_nb_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
190                                            p_vcd->vcddev, &p_vcd->p_sectors );
191     free( psz_source );
192     if( p_vcd->i_nb_tracks < 0 )
193         msg_Err( p_input, "unable to count tracks" );
194     else if( p_vcd->i_nb_tracks <= 1 )
195         msg_Err( p_input, "no movie tracks found" );
196     if( p_vcd->i_nb_tracks <= 1)
197     {
198         ioctl_Close( p_this, p_vcd->vcddev );
199         free( p_vcd );
200         return -1;
201     }
202
203     /* Allocate the entry points table */
204     p_vcd->p_entries = malloc( p_vcd->i_nb_tracks * sizeof( int ) );
205
206     if( p_vcd->p_entries == NULL )
207     {
208         msg_Err( p_input, "not enough memory" );
209         ioctl_Close( p_this, p_vcd->vcddev );
210         free( p_vcd );
211     }
212
213     /* Set stream and area data */
214     vlc_mutex_lock( &p_input->stream.stream_lock );
215
216     /* Initialize ES structures */
217     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
218
219     /* disc input method */
220     p_input->stream.i_method = INPUT_METHOD_VCD;
221
222     p_input->stream.i_area_nb = 1;
223
224 #define area p_input->stream.pp_areas
225     for( i = 1 ; i <= p_vcd->i_nb_tracks - 1 ; i++ )
226     {
227         input_AddArea( p_input );
228
229         /* Titles are Program Chains */
230         area[i]->i_id = i;
231
232         /* Absolute start offset and size */
233         area[i]->i_start = (off_t)p_vcd->p_sectors[i] * (off_t)VCD_DATA_SIZE;
234         area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
235                            * (off_t)VCD_DATA_SIZE;
236
237         /* Number of chapters */
238         area[i]->i_part_nb = 0;   /* will be the entry points */
239         area[i]->i_part = 1;
240
241         /* i_plugin_data is used to store which entry point is the first
242          * of the track (area) */
243         area[i]->i_plugin_data = 0;
244     }
245 #undef area
246
247     p_area = p_input->stream.pp_areas[i_title];
248
249     p_vcd->b_valid_ep = 1;
250     if( VCDEntryPoints( p_input ) < 0 )
251     {
252         msg_Warn( p_input, "could read entry points, will not use them" );
253         p_vcd->b_valid_ep = 0;
254     }
255
256     VCDSetArea( p_input, p_area );
257
258     vlc_mutex_unlock( &p_input->stream.stream_lock );
259
260     if( !p_input->psz_demux || !*p_input->psz_demux )
261     {
262         p_input->psz_demux = "ps";
263     }
264
265     return 0;
266 }
267
268 /*****************************************************************************
269  * VCDClose: closes vcd
270  *****************************************************************************/
271 static void VCDClose( vlc_object_t *p_this )
272 {
273     input_thread_t *   p_input = (input_thread_t *)p_this;
274     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
275
276     ioctl_Close( p_this, p_vcd->vcddev );
277     free( p_vcd );
278 }
279
280 /*****************************************************************************
281  * VCDRead: reads from the VCD into PES packets.
282  *****************************************************************************
283  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
284  * bytes.
285  *****************************************************************************/
286 static int VCDRead( input_thread_t * p_input, byte_t * p_buffer,
287                      size_t i_len )
288 {
289     thread_vcd_data_t *     p_vcd;
290     int                     i_blocks;
291     int                     i_index;
292     int                     i_read;
293     byte_t                  p_last_sector[ VCD_DATA_SIZE ];
294
295     p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
296
297     i_read = 0;
298
299     /* Compute the number of blocks we have to read */
300
301     i_blocks = i_len / VCD_DATA_SIZE;
302
303     for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
304     {
305         if ( ioctl_ReadSector( VLC_OBJECT(p_input), p_vcd->vcddev,
306                     p_vcd->i_sector, p_buffer + i_index * VCD_DATA_SIZE ) < 0 )
307         {
308             msg_Err( p_input, "could not read sector %d", p_vcd->i_sector );
309             return -1;
310         }
311
312         p_vcd->i_sector ++;
313         if ( p_vcd->i_sector == p_vcd->p_sectors[p_vcd->i_track + 1] )
314         {
315             input_area_t *p_area;
316
317             if ( p_vcd->i_track >= p_vcd->i_nb_tracks - 1 )
318                 return 0; /* EOF */
319
320             vlc_mutex_lock( &p_input->stream.stream_lock );
321             p_area = p_input->stream.pp_areas[
322                     p_input->stream.p_selected_area->i_id + 1 ];
323
324             msg_Dbg( p_input, "new title" );
325
326             p_area->i_part = 1;
327             VCDSetArea( p_input, p_area );
328             vlc_mutex_unlock( &p_input->stream.stream_lock );
329         }
330
331         /* Update chapter */
332         else if( p_vcd->b_valid_ep &&
333                 /* FIXME kludge so that read does not update chapter
334                  * when a manual chapter change was requested and not
335                  * yet accomplished */
336                 !p_input->stream.p_new_area )
337         {
338             int i_entry;
339
340             vlc_mutex_lock( &p_input->stream.stream_lock );
341             i_entry = p_input->stream.p_selected_area->i_plugin_data
342                 /* 1st entry point of the track (area)*/
343                         + p_input->stream.p_selected_area->i_part - 1;
344             if( i_entry + 1 < p_vcd->i_entries_nb &&
345                     p_vcd->i_sector >= p_vcd->p_entries[i_entry + 1] )
346             {
347                 msg_Dbg( p_input, "new chapter" );
348                 p_input->stream.p_selected_area->i_part ++;
349             }
350             vlc_mutex_unlock( &p_input->stream.stream_lock );
351         }
352
353         i_read += VCD_DATA_SIZE;
354     }
355
356     if ( i_len % VCD_DATA_SIZE ) /* this should not happen */
357     {
358         if ( ioctl_ReadSector( VLC_OBJECT(p_input), p_vcd->vcddev,
359                                p_vcd->i_sector, p_last_sector ) < 0 )
360         {
361             msg_Err( p_input, "could not read sector %d", p_vcd->i_sector );
362             return -1;
363         }
364
365         p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * VCD_DATA_SIZE,
366                                    p_last_sector, i_len % VCD_DATA_SIZE );
367         i_read += i_len % VCD_DATA_SIZE;
368     }
369
370     return i_read;
371 }
372
373
374 /*****************************************************************************
375  * VCDSetProgram: Does nothing since a VCD is mono_program
376  *****************************************************************************/
377 static int VCDSetProgram( input_thread_t * p_input,
378                           pgrm_descriptor_t * p_program)
379 {
380     return 0;
381 }
382
383
384 /*****************************************************************************
385  * VCDSetArea: initialize input data for title x, chapter y.
386  * It should be called for each user navigation request.
387  ****************************************************************************/
388 static int VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
389 {
390     thread_vcd_data_t *     p_vcd;
391
392     p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
393
394     /* we can't use the interface slider until initilization is complete */
395     p_input->stream.b_seekable = 0;
396
397     if( p_area != p_input->stream.p_selected_area )
398     {
399         /* Reset the Chapter position of the current title */
400         p_input->stream.p_selected_area->i_part = 1;
401         p_input->stream.p_selected_area->i_tell = 0;
402
403         /* Change the default area */
404         p_input->stream.p_selected_area = p_area;
405
406         /* Change the current track */
407         /* The first track is not a valid one  */
408         p_vcd->i_track = p_area->i_id;
409         p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track];
410     }
411
412     if( p_vcd->b_valid_ep )
413     {
414         int i_entry = p_area->i_plugin_data /* 1st entry point of
415                                                the track (area)*/
416                             + p_area->i_part - 1;
417         p_vcd->i_sector = p_vcd->p_entries[i_entry];
418     }
419     else
420         p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track];
421
422     p_input->stream.p_selected_area->i_tell =
423         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
424          - p_input->stream.p_selected_area->i_start;
425
426     /* warn interface that something has changed */
427     p_input->stream.b_seekable = 1;
428     p_input->stream.b_changed = 1;
429
430     return 0;
431 }
432
433
434 /****************************************************************************
435  * VCDSeek
436  ****************************************************************************/
437 static void VCDSeek( input_thread_t * p_input, off_t i_off )
438 {
439     thread_vcd_data_t * p_vcd;
440     unsigned int i_index;
441
442     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
443
444     p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track]
445                        + i_off / (off_t)VCD_DATA_SIZE;
446
447     vlc_mutex_lock( &p_input->stream.stream_lock );
448 #define p_area p_input->stream.p_selected_area
449     /* Find chapter */
450     if( p_vcd->b_valid_ep )
451     {
452         for( i_index = 0 ; i_index < p_area->i_part_nb - 1 ; i_index ++ )
453         {
454             if( p_vcd->i_sector < p_vcd->p_entries[p_area->i_plugin_data
455                 + i_index + 1] )
456             {
457                 p_area->i_part = i_index;
458                 break;
459             }
460         }
461     }
462 #undef p_area
463
464     p_input->stream.p_selected_area->i_tell =
465         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
466          - p_input->stream.p_selected_area->i_start;
467     vlc_mutex_unlock( &p_input->stream.stream_lock );
468 }
469
470 /*****************************************************************************
471  * VCDEntryPoints: Reads the information about the entry points on the disc.
472  *****************************************************************************/
473 static int VCDEntryPoints( input_thread_t * p_input )
474 {
475     thread_vcd_data_t *               p_vcd;
476     byte_t *                          p_sector;
477     entries_sect_t                    entries;
478     uint16_t                          i_nb;
479     int                               i, i_entry_index = 0;
480     int                               i_previous_track = -1;
481
482     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
483
484     p_sector = malloc( VCD_DATA_SIZE * sizeof( byte_t ) );
485     if( p_sector == NULL )
486     {
487         msg_Err( p_input, "not enough memory for entry points treatment" );
488         return -1;
489     }
490
491     if( ioctl_ReadSector( VLC_OBJECT(p_input), p_vcd->vcddev,
492                                 VCD_ENTRIES_SECTOR, p_sector ) < 0 )
493     {
494         msg_Err( p_input, "could not read entry points sector" );
495         free( p_sector );
496         return( -1 );
497     }
498
499     memcpy( &entries, p_sector, CD_SECTOR_SIZE );
500     free( p_sector );
501
502     if( (i_nb = U16_AT( &entries.i_entries_nb )) > 500 )
503     {
504         msg_Err( p_input, "invalid entry points number" );
505         return( -1 );
506     }
507
508     p_vcd->p_entries = malloc( sizeof( int ) * i_nb );
509     if( p_vcd->p_entries == NULL )
510     {
511         msg_Err( p_input, "not enough memory for entry points treatment" );
512         return -1;
513     }
514
515     if( strncmp( entries.psz_id, "ENTRYVCD", sizeof( entries.psz_id ) )
516      && strncmp( entries.psz_id, "ENTRYSVD", sizeof( entries.psz_id ) ))
517     {
518         msg_Err( p_input, "unrecognized entry points format" );
519         free( p_vcd->p_entries );
520         return -1;
521     }
522
523     p_vcd->i_entries_nb = 0;
524
525 #define i_track entries.entry[i].i_track
526     for( i = 0 ; i < i_nb ; i++ )
527     {
528         if( i_track <= p_input->stream.i_area_nb )
529         {
530             p_vcd->p_entries[i_entry_index] =
531                 (MSF_TO_LBA2( BCD_TO_BIN( entries.entry[i].msf.minute ),
532                               BCD_TO_BIN( entries.entry[i].msf.second ),
533                               BCD_TO_BIN( entries.entry[i].msf.frame  ) ));
534             p_input->stream.pp_areas[i_track-1]->i_part_nb ++;
535             /* if this entry belongs to a new track */
536             if( i_track != i_previous_track )
537             {
538                 /* i_plugin_data is used to store the first entry of the area*/
539                 p_input->stream.pp_areas[i_track-1]->i_plugin_data =
540                                                             i_entry_index;
541                 i_previous_track = i_track;
542             }
543             i_entry_index ++;
544             p_vcd->i_entries_nb ++;
545         }
546         else
547             msg_Warn( p_input, "wrong track number found in entry points" );
548     }
549 #undef i_track
550     return 0;
551 }