]> git.sesse.net Git - vlc/blob - plugins/vcd/input_vcd.c
* ./extras/MacOSX_dvdioctl: removed outdated files.
[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 <sys/stat.h>
38 #include <string.h>
39 #include <errno.h>
40
41 #if defined( WIN32 )
42 #   include <io.h>                                                 /* read() */
43 #else
44 #   include <sys/uio.h>                                      /* struct iovec */
45 #endif
46
47 #if defined( WIN32 )
48 #   include "input_iovec.h"
49 #endif
50
51 #include "stream_control.h"
52 #include "input_ext-intf.h"
53 #include "input_ext-dec.h"
54 #include "input_ext-plugins.h"
55
56 #include "debug.h"
57
58 #include "input_vcd.h"
59 #include "cdrom_tools.h"
60
61 /* how many blocks VCDRead will read in each loop */
62 #define VCD_BLOCKS_ONCE 20
63 #define VCD_DATA_ONCE   (VCD_BLOCKS_ONCE * VCD_DATA_SIZE)
64
65 /*****************************************************************************
66  * Local prototypes
67  *****************************************************************************/
68 /* called from outside */
69 static int  VCDInit         ( struct input_thread_s * );
70 static void VCDEnd          ( struct input_thread_s * );
71 static int  VCDDemux        ( struct input_thread_s * );
72 static int  VCDRewind       ( struct input_thread_s * );
73
74 static int  VCDOpen         ( struct input_thread_s *);
75 static void VCDClose        ( struct input_thread_s *);
76 static int  VCDRead         ( struct input_thread_s *, byte_t *, size_t );
77 static void VCDSeek         ( struct input_thread_s *, off_t );
78 static int  VCDSetArea      ( struct input_thread_s *, struct input_area_s * );
79 static int  VCDSetProgram   ( struct input_thread_s *, pgrm_descriptor_t * );
80
81 /*****************************************************************************
82  * Functions exported as capabilities. They are declared as static so that
83  * we don't pollute the namespace too much.
84  *****************************************************************************/
85 void _M( access_getfunctions )( function_list_t * p_function_list )
86 {
87 #define access p_function_list->functions.access
88     access.pf_open             = VCDOpen;
89     access.pf_close            = VCDClose;
90     access.pf_read             = VCDRead;
91     access.pf_set_area         = VCDSetArea;
92     access.pf_set_program      = VCDSetProgram;
93     access.pf_seek             = VCDSeek;
94 #undef access
95 }
96
97
98 void _M( demux_getfunctions )( function_list_t * p_function_list )
99 {
100 #define demux p_function_list->functions.demux
101     demux.pf_init             = VCDInit;
102     demux.pf_end              = VCDEnd;
103     demux.pf_demux            = VCDDemux;
104     demux.pf_rewind           = VCDRewind;
105 #undef demux
106 }
107
108 /*
109  * Data reading functions
110  */
111
112 /*****************************************************************************
113  * VCDOpen: open vcd
114  *****************************************************************************/
115 static int VCDOpen( struct input_thread_s *p_input )
116 {
117     char *                  psz_orig;
118     char *                  psz_parser;
119     char *                  psz_source;
120     char *                  psz_next;
121     struct stat             stat_info;
122     thread_vcd_data_t *     p_vcd;
123     int                     i;
124     input_area_t *          p_area;
125     int                     i_title = 1;
126     int                     i_chapter = 1;
127
128     
129
130     /* parse the options passed in command line : */
131     psz_orig = psz_parser = psz_source = strdup( p_input->psz_name );
132     
133     if( !psz_orig )
134     {
135         return( -1 );
136     }
137  
138     while( *psz_parser && *psz_parser != '@' )
139     {
140         psz_parser++;
141     }
142
143     if( *psz_parser == '@' )
144     {
145         /* Found options */
146         *psz_parser = '\0';
147         ++psz_parser;
148
149         i_title = (int)strtol( psz_parser, &psz_next, 10 );
150         if( *psz_next )
151         {
152             psz_parser = psz_next + 1;
153             i_chapter = (int)strtol( psz_parser, &psz_next, 10 );
154         }
155
156         i_title = i_title ? i_title : 1;
157         i_chapter = i_chapter ? i_chapter : 1;
158     }
159
160     if( !*psz_source )
161     {
162         if( !p_input->psz_access )
163         {
164             free( psz_orig );
165             return -1;
166         }
167         psz_source = config_GetPszVariable( "vcd_device" );
168     }
169
170     /* test the type of file given */
171     
172     if( stat( psz_source, &stat_info ) == -1 )
173     {
174         intf_ErrMsg( "input: vcd: cannot stat() source `%s' (%s)",
175                      psz_source, strerror(errno));
176         return( -1 );
177     }
178     
179     if( !S_ISBLK(stat_info.st_mode) )
180     {
181         intf_WarnMsg( 3, "input : VCD plugin discarded"
182                          " (not a valid drive)" );
183         return -1;
184     }
185     
186     
187     p_vcd = malloc( sizeof(thread_vcd_data_t) );
188
189     if( p_vcd == NULL )
190     {
191         intf_ErrMsg( "vcd error: out of memory" );
192         return -1;
193     }
194     
195     p_input->p_access_data = (void *)p_vcd;
196     
197     p_input->i_mtu = VCD_DATA_ONCE;
198    
199     vlc_mutex_lock( &p_input->stream.stream_lock );
200
201     p_input->stream.b_pace_control = 1;
202
203     p_input->stream.b_seekable = 1;
204     p_input->stream.p_selected_area->i_size = 0;
205     p_input->stream.p_selected_area->i_tell = 0;
206
207     vlc_mutex_unlock( &p_input->stream.stream_lock );
208
209     p_vcd->i_handle = open( psz_source, O_RDONLY | O_NONBLOCK );
210
211     if( p_vcd->i_handle == -1 )
212     {
213         intf_ErrMsg( "input: vcd: Could not open %s\n", psz_source );
214         free (p_vcd);
215         return -1;
216     }
217
218     /* We read the Table Of Content information */
219     p_vcd->nb_tracks = ioctl_GetTrackCount( p_vcd->i_handle,
220                                             psz_source );
221     if( p_vcd->nb_tracks < 0 )
222     {
223         intf_ErrMsg( "input: vcd: was unable to count tracks" );
224         close( p_vcd->i_handle );
225         free( p_vcd );
226         return -1;
227     }
228     else if( p_vcd->nb_tracks <= 1 )
229     {
230         intf_ErrMsg( "input: vcd: no movie tracks found" );
231         close( p_vcd->i_handle );
232         free( p_vcd );
233         return -1;
234     }
235
236     p_vcd->p_sectors = ioctl_GetSectors( p_vcd->i_handle,
237                                          psz_source );
238     if( p_vcd->p_sectors == NULL )
239     {
240         input_BuffersEnd( p_input->p_method_data );
241         close( p_vcd->i_handle );
242         free( p_vcd );
243         return -1;
244     }
245
246     /* Set stream and area data */
247     vlc_mutex_lock( &p_input->stream.stream_lock );
248
249     /* Initialize ES structures */
250     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
251
252     /* disc input method */
253     p_input->stream.i_method = INPUT_METHOD_VCD;
254
255 #define area p_input->stream.pp_areas
256     for( i = 1 ; i <= p_vcd->nb_tracks - 1 ; i++ )
257     {
258         input_AddArea( p_input );
259
260         /* Titles are Program Chains */
261         area[i]->i_id = i;
262
263         /* Absolute start offset and size */
264         area[i]->i_start = (off_t)p_vcd->p_sectors[i] * (off_t)VCD_DATA_SIZE;
265         area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
266                            * (off_t)VCD_DATA_SIZE;
267
268         /* Number of chapters */
269         area[i]->i_part_nb = 0;   // will be the entry points
270         area[i]->i_part = 1;
271
272         area[i]->i_plugin_data = p_vcd->p_sectors[i];
273     }
274 #undef area
275
276     p_area = p_input->stream.pp_areas[i_title];
277
278     VCDSetArea( p_input, p_area );
279
280     vlc_mutex_unlock( &p_input->stream.stream_lock );
281
282     p_input->psz_demux = "vcd";
283
284     return 0;
285 }
286
287 /*****************************************************************************
288  * VCDClose: closes vcd
289  *****************************************************************************/
290 static void VCDClose( struct input_thread_s *p_input )
291 {
292     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
293
294     close( p_vcd->i_handle );
295     free( p_vcd );
296 }
297
298 /*****************************************************************************
299  * VCDRead: reads from the VCD into PES packets.
300  *****************************************************************************
301  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
302  * bytes.
303  *****************************************************************************/
304 static int VCDRead( input_thread_t * p_input, byte_t * p_buffer, 
305                      size_t i_len )
306 {
307     thread_vcd_data_t *     p_vcd;
308     int                     i_blocks;
309     int                     i_index;
310     int                     i_read;
311     byte_t                  p_last_sector[ VCD_DATA_SIZE ];
312
313     p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
314
315     i_read = 0;
316
317     /* Compute the number of blocks we have to read */
318
319     i_blocks = i_len / VCD_DATA_SIZE;
320
321     for ( i_index = 0 ; i_index < i_blocks ; i_index++ ) 
322     {
323         if ( ioctl_ReadSector( p_vcd->i_handle, p_vcd->i_sector, 
324                     p_buffer + i_index * VCD_DATA_SIZE ) < 0 )
325         {
326             intf_ErrMsg( "input: vcd: could not read sector %d\n", 
327                     p_vcd->i_sector );
328             return -1;
329         }
330
331         p_vcd->i_sector ++;
332         if ( p_vcd->i_sector == p_vcd->p_sectors[p_vcd->i_track + 1] )
333         {
334             input_area_t *p_area;
335             
336             if ( p_vcd->i_track >= p_vcd->nb_tracks - 1 )
337                 return 0; /* EOF */
338             
339             p_area = p_input->stream.pp_areas[
340                     p_input->stream.p_selected_area->i_id + 1 ];
341             
342             intf_WarnMsg( 4, "input: vcd info: new title" );
343             
344             p_area->i_part = 1;
345             VCDSetArea( p_input, p_area );
346     
347         }
348         i_read += VCD_DATA_SIZE;
349     }
350     
351     if ( i_len % VCD_DATA_SIZE ) /* this should not happen */
352     { 
353         if ( ioctl_ReadSector( p_vcd->i_handle, p_vcd->i_sector, 
354                     p_last_sector ) < 0 )
355         {
356             intf_ErrMsg( "input: vcd: could not read sector %d\n", 
357                     p_vcd->i_sector );
358             return -1;
359         }
360         
361         FAST_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     p_input->stream.p_selected_area->i_tell = 
367         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
368          - p_input->stream.p_selected_area->i_start;
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     /* warn interface that something has changed */
413     p_input->stream.b_seekable = 1;
414     p_input->stream.b_changed = 1;
415
416     return 0;
417 }
418
419
420 /*****************************************************************************
421  * VCDRewind : reads a stream backward
422  *****************************************************************************/
423 static int VCDRewind( input_thread_t * p_input )
424 {
425     return( -1 );
426 }
427
428 /****************************************************************************
429  * VCDSeek
430  ****************************************************************************/
431 static void VCDSeek( input_thread_t * p_input, off_t i_off )
432 {
433     thread_vcd_data_t *               p_vcd;
434
435     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
436
437     p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track]
438                        + i_off / (off_t)VCD_DATA_SIZE;
439
440     p_input->stream.p_selected_area->i_tell = 
441         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
442          - p_input->stream.p_selected_area->i_start;
443 }
444
445 /*
446  * Demux functions
447  */
448
449
450 /*****************************************************************************
451  * VCDInit: initializes VCD structures
452  *****************************************************************************/
453 static int VCDInit( input_thread_t * p_input )
454 {
455     es_descriptor_t *       p_es;
456     
457     if( p_input->stream.i_method != INPUT_METHOD_VCD )
458     {
459         return -1;
460     }
461
462     vlc_mutex_lock( &p_input->stream.stream_lock );
463     
464     /* Set program information. */
465     input_AddProgram( p_input, 0, sizeof( stream_ps_data_t ) );
466     p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
467
468     /* No PSM to read in disc mode, we already have all the information */
469     p_input->stream.p_selected_program->b_is_ok = 1;
470
471     p_es = input_AddES( p_input, p_input->stream.p_selected_program, 0xe0, 0 );
472     p_es->i_stream_id = 0xe0;
473     p_es->i_type = MPEG1_VIDEO_ES;
474     p_es->i_cat = VIDEO_ES;
475
476     if( p_main->b_video )
477     {
478         input_SelectES( p_input, p_es );
479     }
480
481     p_es = input_AddES( p_input, p_input->stream.p_selected_program, 0xc0, 0 );
482     p_es->i_stream_id = 0xc0;
483     p_es->i_type = MPEG1_AUDIO_ES;
484     p_es->b_audio = 1;
485     p_es->i_cat = AUDIO_ES;
486
487     if( p_main->b_audio )
488     {
489         input_SelectES( p_input, p_es );
490     }
491
492     vlc_mutex_unlock( &p_input->stream.stream_lock );
493     
494     return 0;
495 }
496
497 /*****************************************************************************
498  * VCDEnd: frees unused data
499  *****************************************************************************/
500 static void VCDEnd( input_thread_t * p_input )
501 {
502     ;
503 }
504
505
506 /*****************************************************************************
507  * VCDDemux: reads and demuxes data packets
508  *****************************************************************************
509  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
510  * packets.
511  *****************************************************************************/
512 static int VCDDemux( input_thread_t * p_input )
513 {
514     int                 i;
515
516     for( i = 0; i < VCD_BLOCKS_ONCE; i++ )
517     {
518         data_packet_t *     p_data;
519         ssize_t             i_result;
520
521         i_result = input_ReadPS( p_input, &p_data );
522
523         if( i_result <= 0 )
524         {
525             return( i_result );
526         }
527
528         input_DemuxPS( p_input, p_data );
529     }
530
531     return( i );
532 }
533