]> git.sesse.net Git - vlc/blob - plugins/vcd/input_vcd.c
* added some tests in open
[vlc] / plugins / vcd / input_vcd.c
1 /****************************************************************************
2  * input_vcd.c: VideoCD raw reading plugin.
3  *****************************************************************************
4  * Copyright (C) 1998-2001 VideoLAN
5  *
6  * Author: Johan Bilien <jobi@via.ecp.fr>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  * 
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
21  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26 #include <stdio.h>
27 #include <stdlib.h>
28
29 #include <videolan/vlc.h>
30
31 #ifdef HAVE_UNISTD_H
32 #   include <unistd.h>
33 #endif
34
35 #include <fcntl.h>
36 #include <sys/types.h>
37 #include <string.h>
38 #include <errno.h>
39
40 #ifdef STRNCASECMP_IN_STRINGS_H
41 #   include <strings.h>
42 #endif
43
44 #if defined( WIN32 )
45 #   include <io.h>                                                 /* read() */
46 #else
47 #   include <sys/uio.h>                                      /* struct iovec */
48 #endif
49
50 #if defined( WIN32 )
51 #   include "input_iovec.h"
52 #endif
53
54 #include "stream_control.h"
55 #include "input_ext-intf.h"
56 #include "input_ext-dec.h"
57 #include "input_ext-plugins.h"
58
59 #include "debug.h"
60
61 #include "input_vcd.h"
62 #include "cdrom_tools.h"
63
64 /* how many blocks VCDRead will read in each loop */
65 #define VCD_BLOCKS_ONCE 20
66 #define VCD_DATA_ONCE   (VCD_BLOCKS_ONCE * VCD_DATA_SIZE)
67
68 /*****************************************************************************
69  * Local prototypes
70  *****************************************************************************/
71 /* called from outside */
72 static int  VCDInit         ( struct input_thread_s * );
73 static void VCDEnd          ( struct input_thread_s * );
74 static int  VCDDemux        ( struct input_thread_s * );
75 static int  VCDRewind       ( struct input_thread_s * );
76
77 static int  VCDOpen         ( struct input_thread_s *);
78 static void VCDClose        ( struct input_thread_s *);
79 static int  VCDRead         ( struct input_thread_s *, byte_t *, size_t );
80 static void VCDSeek         ( struct input_thread_s *, off_t );
81 static int  VCDSetArea      ( struct input_thread_s *, struct input_area_s * );
82 static int  VCDSetProgram   ( struct input_thread_s *, pgrm_descriptor_t * );
83
84 /*****************************************************************************
85  * Functions exported as capabilities. They are declared as static so that
86  * we don't pollute the namespace too much.
87  *****************************************************************************/
88 void _M( access_getfunctions )( function_list_t * p_function_list )
89 {
90 #define access p_function_list->functions.access
91     access.pf_open             = VCDOpen;
92     access.pf_close            = VCDClose;
93     access.pf_read             = VCDRead;
94     access.pf_set_area         = VCDSetArea;
95     access.pf_set_program      = VCDSetProgram;
96     access.pf_seek             = VCDSeek;
97 #undef access
98 }
99
100
101 void _M( demux_getfunctions )( function_list_t * p_function_list )
102 {
103 #define demux p_function_list->functions.demux
104     demux.pf_init             = VCDInit;
105     demux.pf_end              = VCDEnd;
106     demux.pf_demux            = VCDDemux;
107     demux.pf_rewind           = VCDRewind;
108 #undef demux
109 }
110
111 /*
112  * Data reading functions
113  */
114
115 /*****************************************************************************
116  * VCDOpen: open vcd
117  *****************************************************************************/
118 static int VCDOpen( struct input_thread_s *p_input )
119 {
120     char *                  psz_orig;
121     char *                  psz_parser;
122     char *                  psz_source;
123     char *                  psz_next;
124     struct stat             stat_info;
125     thread_vcd_data_t *     p_vcd;
126     int                     i;
127     input_area_t *          p_area;
128     int                     i_title = 1;
129     int                     i_chapter = 1;
130
131     
132
133     /* parse the options passed in command line : */
134     psz_orig = psz_parser = psz_source = strdup( p_input->psz_name );
135     
136     if( !psz_orig )
137     {
138         return( -1 );
139     }
140  
141     while( *psz_parser && *psz_parser != '@' )
142     {
143         psz_parser++;
144     }
145
146     if( *psz_parser == '@' )
147     {
148         /* Found options */
149         *psz_parser = '\0';
150         ++psz_parser;
151
152         i_title = (int)strtol( psz_parser, &psz_next, 10 );
153         if( *psz_next )
154         {
155             psz_parser = psz_next + 1;
156             i_chapter = (int)strtol( psz_parser, &psz_next, 10 );
157         }
158
159         i_title = i_title ? i_title : 1;
160         i_chapter = i_chapter ? i_chapter : 1;
161     }
162
163     if( !*psz_source )
164     {
165         if( !p_input->psz_access )
166         {
167             free( psz_orig );
168             return -1;
169         }
170         psz_source = config_GetPszVariable( INPUT_VCD_DEVICE_VAR );
171     }
172
173     /* test the type of file given */
174     
175     if( stat( psz_source, &stat_info ) == -1 )
176     {
177         intf_ErrMsg( "input: vcd: cannot stat() source `%s' (%s)",
178                      psz_source, strerror(errno));
179         return( -1 );
180     }
181     
182     if( !S_ISBLK(stat_info.st_mode) )
183     {
184         intf_WarnMsg( 3, "input : VCD plugin discarded"
185                          " (not a valid drive)" );
186         return -1;
187     }
188     
189     
190     p_vcd = malloc( sizeof(thread_vcd_data_t) );
191
192     if( p_vcd == NULL )
193     {
194         intf_ErrMsg( "vcd error: out of memory" );
195         return -1;
196     }
197     
198     p_input->p_access_data = (void *)p_vcd;
199     
200     p_input->i_mtu = VCD_DATA_ONCE;
201    
202     vlc_mutex_lock( &p_input->stream.stream_lock );
203
204     p_input->stream.b_pace_control = 1;
205
206     p_input->stream.b_seekable = 1;
207     p_input->stream.p_selected_area->i_size = 0;
208     p_input->stream.p_selected_area->i_tell = 0;
209
210     vlc_mutex_unlock( &p_input->stream.stream_lock );
211
212     p_vcd->i_handle = open( psz_source, O_RDONLY | O_NONBLOCK );
213
214     if( p_vcd->i_handle == -1 )
215     {
216         intf_ErrMsg( "input: vcd: Could not open %s\n", psz_source );
217         free (p_vcd);
218         return -1;
219     }
220
221     /* We read the Table Of Content information */
222     p_vcd->nb_tracks = ioctl_GetTrackCount( p_vcd->i_handle,
223                                             psz_source );
224     if( p_vcd->nb_tracks < 0 )
225     {
226         intf_ErrMsg( "input: vcd: was unable to count tracks" );
227         close( p_vcd->i_handle );
228         free( p_vcd );
229         return -1;
230     }
231     else if( p_vcd->nb_tracks <= 1 )
232     {
233         intf_ErrMsg( "input: vcd: no movie tracks found" );
234         close( p_vcd->i_handle );
235         free( p_vcd );
236         return -1;
237     }
238
239     p_vcd->p_sectors = ioctl_GetSectors( p_vcd->i_handle,
240                                          psz_source );
241     if ( p_vcd->p_sectors == NULL )
242     {
243         close( p_vcd->i_handle );
244         free( p_vcd );
245         return -1;
246     }
247
248     /* Set stream and area data */
249     vlc_mutex_lock( &p_input->stream.stream_lock );
250
251     /* Initialize ES structures */
252     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
253
254     /* disc input method */
255     p_input->stream.i_method = INPUT_METHOD_VCD;
256
257 #define area p_input->stream.pp_areas
258     for( i = 1 ; i <= p_vcd->nb_tracks - 1 ; i++ )
259     {
260         input_AddArea( p_input );
261
262         /* Titles are Program Chains */
263         area[i]->i_id = i;
264
265         /* Absolute start offset and size */
266         area[i]->i_start = (off_t)p_vcd->p_sectors[i] * (off_t)VCD_DATA_SIZE;
267         area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
268                            * (off_t)VCD_DATA_SIZE;
269
270         /* Number of chapters */
271         area[i]->i_part_nb = 0;   // will be the entry points
272         area[i]->i_part = 1;
273
274         area[i]->i_plugin_data = p_vcd->p_sectors[i];
275     }
276 #undef area
277
278     p_area = p_input->stream.pp_areas[i_title];
279
280     VCDSetArea( p_input, p_area );
281
282     vlc_mutex_unlock( &p_input->stream.stream_lock );
283
284     p_input->psz_demux = "vcd";
285     
286     return 0;
287 }
288
289     
290
291 /*****************************************************************************
292  * VCDClose: closes vcd
293  *****************************************************************************/
294 static void VCDClose( struct input_thread_s *p_input )
295 {
296     thread_vcd_data_t *     p_vcd
297         = (thread_vcd_data_t *)p_input->p_access_data;
298     close( p_vcd->i_handle );
299     free( p_vcd );
300 }
301
302 /*****************************************************************************
303  * VCDRead: reads from the VCD into PES packets.
304  *****************************************************************************
305  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
306  * bytes.
307  *****************************************************************************/
308 static int VCDRead( input_thread_t * p_input, byte_t * p_buffer, 
309                      size_t i_len )
310 {
311     thread_vcd_data_t *     p_vcd;
312     int                     i_blocks;
313     int                     i_index;
314     int                     i_read;
315     byte_t                  p_last_sector[ VCD_DATA_SIZE ];
316
317     p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
318
319     i_read = 0;
320
321     /* Compute the number of blocks we have to read */
322
323     i_blocks = i_len / VCD_DATA_SIZE;
324
325     for ( i_index = 0 ; i_index < i_blocks ; i_index++ ) 
326     {
327         if ( ioctl_ReadSector( p_vcd->i_handle, p_vcd->i_sector, 
328                     p_buffer + i_index * VCD_DATA_SIZE ) < 0 )
329         {
330             intf_ErrMsg( "input: vcd: could not read sector %d\n", 
331                     p_vcd->i_sector );
332             return -1;
333         }
334
335         p_vcd->i_sector ++;
336         if ( p_vcd->i_sector == p_vcd->p_sectors[p_vcd->i_track + 1] )
337         {
338             input_area_t *p_area;
339             
340             if ( p_vcd->i_track >= p_vcd->nb_tracks - 1 )
341                 return 0; /* EOF */
342             
343             p_area = p_input->stream.pp_areas[
344                     p_input->stream.p_selected_area->i_id + 1 ];
345             
346             intf_WarnMsg( 4, "input: vcd info: new title" );
347             
348             p_area->i_part = 1;
349             VCDSetArea( p_input, p_area );
350     
351         }
352         i_read += VCD_DATA_SIZE;
353     }
354     
355     if ( i_len % VCD_DATA_SIZE ) /* this should not happen */
356     { 
357         if ( ioctl_ReadSector( p_vcd->i_handle, p_vcd->i_sector, 
358                     p_last_sector ) < 0 )
359         {
360             intf_ErrMsg( "input: vcd: could not read sector %d\n", 
361                     p_vcd->i_sector );
362             return -1;
363         }
364         
365         FAST_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     p_input->stream.p_selected_area->i_tell = 
371         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
372          - p_input->stream.p_selected_area->i_start;
373
374     return i_read;
375 }
376
377
378 /*****************************************************************************
379  * VCDSetProgram: Does nothing since a VCD is mono_program
380  *****************************************************************************/
381 static int VCDSetProgram( input_thread_t * p_input,
382                           pgrm_descriptor_t * p_program)
383 {
384     return 0;
385 }
386
387
388 /*****************************************************************************
389  * VCDSetArea: initialize input data for title x, chapter y.
390  * It should be called for each user navigation request.
391  ****************************************************************************/
392 static int VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
393 {
394     thread_vcd_data_t *     p_vcd;
395
396     p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
397
398     /* we can't use the interface slider until initilization is complete */
399     p_input->stream.b_seekable = 0;
400
401     if( p_area != p_input->stream.p_selected_area )
402     {
403         /* Reset the Chapter position of the current title */
404         p_input->stream.p_selected_area->i_part = 1;
405         p_input->stream.p_selected_area->i_tell = 0;
406
407         /* Change the default area */
408         p_input->stream.p_selected_area = p_area;
409
410         /* Change the current track */
411         /* The first track is not a valid one  */
412         p_vcd->i_track = p_area->i_id;
413         p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track];
414     }
415
416     /* warn interface that something has changed */
417     p_input->stream.b_seekable = 1;
418     p_input->stream.b_changed = 1;
419
420     return 0;
421 }
422
423
424 /*****************************************************************************
425  * VCDRewind : reads a stream backward
426  *****************************************************************************/
427 static int VCDRewind( input_thread_t * p_input )
428 {
429     return( -1 );
430 }
431
432 /****************************************************************************
433  * VCDSeek
434  ****************************************************************************/
435 static void VCDSeek( input_thread_t * p_input, off_t i_off )
436 {
437     thread_vcd_data_t *               p_vcd;
438
439     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
440
441     p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track]
442                        + i_off / (off_t)VCD_DATA_SIZE;
443
444     p_input->stream.p_selected_area->i_tell = 
445         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
446          - p_input->stream.p_selected_area->i_start;
447 }
448
449 /*
450  * Demux functions
451  */
452
453
454 /*****************************************************************************
455  * VCDInit: initializes VCD structures
456  *****************************************************************************/
457 static int VCDInit( input_thread_t * p_input )
458 {
459     es_descriptor_t *       p_es;
460     
461     vlc_mutex_lock( &p_input->stream.stream_lock );
462     
463     /* Set program information. */
464     input_AddProgram( p_input, 0, sizeof( stream_ps_data_t ) );
465     p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
466
467     /* No PSM to read in disc mode, we already have all the information */
468     p_input->stream.p_selected_program->b_is_ok = 1;
469
470     p_es = input_AddES( p_input, p_input->stream.p_selected_program, 0xe0, 0 );
471     p_es->i_stream_id = 0xe0;
472     p_es->i_type = MPEG1_VIDEO_ES;
473     p_es->i_cat = VIDEO_ES;
474
475     if( p_main->b_video )
476     {
477         input_SelectES( p_input, p_es );
478     }
479
480     p_es = input_AddES( p_input, p_input->stream.p_selected_program, 0xc0, 0 );
481     p_es->i_stream_id = 0xc0;
482     p_es->i_type = MPEG1_AUDIO_ES;
483     p_es->b_audio = 1;
484     p_es->i_cat = AUDIO_ES;
485
486     if( p_main->b_audio )
487     {
488         input_SelectES( p_input, p_es );
489     }
490
491     vlc_mutex_unlock( &p_input->stream.stream_lock );
492     
493     return 0;
494 }
495
496 /*****************************************************************************
497  * VCDEnd: frees unused data
498  *****************************************************************************/
499 static void VCDEnd( input_thread_t * p_input )
500 {
501     thread_vcd_data_t *     p_vcd;
502
503     input_BuffersEnd( p_input->p_method_data );
504
505     p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
506
507     free( p_vcd );
508 }
509
510
511 /*****************************************************************************
512  * VCDDemux: reads and demuxes data packets
513  *****************************************************************************
514  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
515  * packets.
516  *****************************************************************************/
517 static int VCDDemux( input_thread_t * p_input )
518 {
519     int                 i;
520
521     for( i = 0; i < VCD_BLOCKS_ONCE; i++ )
522     {
523         data_packet_t *     p_data;
524         ssize_t             i_result;
525
526         i_result = input_ReadPS( p_input, &p_data );
527
528         if( i_result <= 0 )
529         {
530             return( i_result );
531         }
532
533         input_DemuxPS( p_input, p_data );
534     }
535
536     return( i );
537 }
538