]> git.sesse.net Git - vlc/blob - modules/access/vcd/vcd.c
* ./modules/*: moved plugins to the new tree. Yet untested builds include
[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.1 2002/08/04 17:23:42 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 #ifdef HAVE_UNISTD_H
34 #   include <unistd.h>
35 #endif
36
37 #include <fcntl.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <string.h>
41 #include <errno.h>
42
43 #if defined( WIN32 )
44 #   include <io.h>                                                 /* read() */
45 #else
46 #   include <sys/uio.h>                                      /* struct iovec */
47 #endif
48
49 #if defined( WIN32 )
50 #   include "input_iovec.h"
51 #endif
52
53 #include "vcd.h"
54 #include "cdrom.h"
55
56 /* how many blocks VCDRead will read in each loop */
57 #define VCD_BLOCKS_ONCE 20
58 #define VCD_DATA_ONCE   (VCD_BLOCKS_ONCE * VCD_DATA_SIZE)
59
60 /*****************************************************************************
61  * Local prototypes
62  *****************************************************************************/
63 static int  VCDOpen         ( vlc_object_t * );
64 static void VCDClose        ( vlc_object_t * );
65 static int  VCDRead         ( input_thread_t *, byte_t *, size_t );
66 static void VCDSeek         ( input_thread_t *, off_t );
67 static int  VCDSetArea      ( input_thread_t *, input_area_t * );
68 static int  VCDSetProgram   ( input_thread_t *, pgrm_descriptor_t * );
69
70 /*****************************************************************************
71  * Module descriptior
72  *****************************************************************************/
73 vlc_module_begin();
74     set_description( _("VCD input module") );
75     set_capability( "access", 80 );
76     set_callbacks( VCDOpen, VCDClose );
77     add_shortcut( "svcd" );
78 vlc_module_end();
79
80 /*
81  * Data reading functions
82  */
83
84 /*****************************************************************************
85  * VCDOpen: open vcd
86  *****************************************************************************/
87 static int VCDOpen( vlc_object_t *p_this )
88 {
89     input_thread_t *        p_input = (input_thread_t *)p_this;
90     char *                  psz_orig;
91     char *                  psz_parser;
92     char *                  psz_source;
93     char *                  psz_next;
94     struct stat             stat_info;
95     thread_vcd_data_t *     p_vcd;
96     int                     i;
97     input_area_t *          p_area;
98     int                     i_title = 1;
99     int                     i_chapter = 1;
100
101     p_input->pf_read = VCDRead;
102     p_input->pf_seek = VCDSeek;
103     p_input->pf_set_area = VCDSetArea;
104     p_input->pf_set_program = VCDSetProgram;
105
106     /* parse the options passed in command line : */
107     psz_orig = psz_parser = psz_source = strdup( p_input->psz_name );
108     
109     if( !psz_orig )
110     {
111         return( -1 );
112     }
113  
114     while( *psz_parser && *psz_parser != '@' )
115     {
116         psz_parser++;
117     }
118
119     if( *psz_parser == '@' )
120     {
121         /* Found options */
122         *psz_parser = '\0';
123         ++psz_parser;
124
125         i_title = (int)strtol( psz_parser, &psz_next, 10 );
126         if( *psz_next )
127         {
128             psz_parser = psz_next + 1;
129             i_chapter = (int)strtol( psz_parser, &psz_next, 10 );
130         }
131
132         i_title = i_title ? i_title : 1;
133         i_chapter = i_chapter ? i_chapter : 1;
134     }
135
136     if( !*psz_source )
137     {
138         if( !p_input->psz_access )
139         {
140             free( psz_orig );
141             return -1;
142         }
143         psz_source = config_GetPsz( p_input, "vcd" );
144     }
145
146     /* test the type of file given */
147     
148     if( stat( psz_source, &stat_info ) == -1 )
149     {
150         msg_Err( p_input, "cannot stat() source `%s' (%s)",
151                           psz_source, strerror(errno));
152         return( -1 );
153     }
154     
155     if( !S_ISBLK(stat_info.st_mode) && !S_ISCHR(stat_info.st_mode))
156     {
157         msg_Warn( p_input, "vcd module discarded (not a valid drive)" );
158         return -1;
159     }
160     
161     
162     p_vcd = malloc( sizeof(thread_vcd_data_t) );
163
164     if( p_vcd == NULL )
165     {
166         msg_Err( p_input, "out of memory" );
167         return -1;
168     }
169     
170     p_input->p_access_data = (void *)p_vcd;
171     
172     p_input->i_mtu = VCD_DATA_ONCE;
173    
174     vlc_mutex_lock( &p_input->stream.stream_lock );
175
176     p_input->stream.b_pace_control = 1;
177
178     p_input->stream.b_seekable = 1;
179     p_input->stream.p_selected_area->i_size = 0;
180     p_input->stream.p_selected_area->i_tell = 0;
181
182     vlc_mutex_unlock( &p_input->stream.stream_lock );
183
184     p_vcd->i_handle = open( psz_source, O_RDONLY | O_NONBLOCK );
185
186     if( p_vcd->i_handle == -1 )
187     {
188         msg_Err( p_input, "could not open %s\n", psz_source );
189         free (p_vcd);
190         return -1;
191     }
192
193     /* We read the Table Of Content information */
194     p_vcd->nb_tracks = ioctl_GetTrackCount( p_vcd->i_handle,
195                                             psz_source );
196     if( p_vcd->nb_tracks < 0 )
197     {
198         msg_Err( p_input, "unable to count tracks" );
199         close( p_vcd->i_handle );
200         free( p_vcd );
201         return -1;
202     }
203     else if( p_vcd->nb_tracks <= 1 )
204     {
205         msg_Err( p_input, "no movie tracks found" );
206         close( p_vcd->i_handle );
207         free( p_vcd );
208         return -1;
209     }
210
211     p_vcd->p_sectors = ioctl_GetSectors( p_vcd->i_handle,
212                                          psz_source );
213     if( p_vcd->p_sectors == NULL )
214     {
215         input_BuffersEnd( p_input, p_input->p_method_data );
216         close( p_vcd->i_handle );
217         free( p_vcd );
218         return -1;
219     }
220
221     /* Set stream and area data */
222     vlc_mutex_lock( &p_input->stream.stream_lock );
223
224     /* Initialize ES structures */
225     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
226
227     /* disc input method */
228     p_input->stream.i_method = INPUT_METHOD_VCD;
229
230 #define area p_input->stream.pp_areas
231     for( i = 1 ; i <= p_vcd->nb_tracks - 1 ; i++ )
232     {
233         input_AddArea( p_input );
234
235         /* Titles are Program Chains */
236         area[i]->i_id = i;
237
238         /* Absolute start offset and size */
239         area[i]->i_start = (off_t)p_vcd->p_sectors[i] * (off_t)VCD_DATA_SIZE;
240         area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
241                            * (off_t)VCD_DATA_SIZE;
242
243         /* Number of chapters */
244         area[i]->i_part_nb = 0;   // will be the entry points
245         area[i]->i_part = 1;
246
247         area[i]->i_plugin_data = p_vcd->p_sectors[i];
248     }
249 #undef area
250
251     p_area = p_input->stream.pp_areas[i_title];
252
253     VCDSetArea( p_input, p_area );
254
255     vlc_mutex_unlock( &p_input->stream.stream_lock );
256
257     p_input->psz_demux = "ps";
258
259     return 0;
260 }
261
262 /*****************************************************************************
263  * VCDClose: closes vcd
264  *****************************************************************************/
265 static void VCDClose( vlc_object_t *p_this )
266 {
267     input_thread_t *   p_input = (input_thread_t *)p_this;
268     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
269
270     close( p_vcd->i_handle );
271     free( p_vcd );
272 }
273
274 /*****************************************************************************
275  * VCDRead: reads from the VCD into PES packets.
276  *****************************************************************************
277  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
278  * bytes.
279  *****************************************************************************/
280 static int VCDRead( input_thread_t * p_input, byte_t * p_buffer, 
281                      size_t i_len )
282 {
283     thread_vcd_data_t *     p_vcd;
284     int                     i_blocks;
285     int                     i_index;
286     int                     i_read;
287     byte_t                  p_last_sector[ VCD_DATA_SIZE ];
288
289     p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
290
291     i_read = 0;
292
293     /* Compute the number of blocks we have to read */
294
295     i_blocks = i_len / VCD_DATA_SIZE;
296
297     for ( i_index = 0 ; i_index < i_blocks ; i_index++ ) 
298     {
299         if ( ioctl_ReadSector( p_vcd->i_handle, p_vcd->i_sector, 
300                     p_buffer + i_index * VCD_DATA_SIZE ) < 0 )
301         {
302             msg_Err( p_input, "could not read sector %d", p_vcd->i_sector );
303             return -1;
304         }
305
306         p_vcd->i_sector ++;
307         if ( p_vcd->i_sector == p_vcd->p_sectors[p_vcd->i_track + 1] )
308         {
309             input_area_t *p_area;
310             
311             if ( p_vcd->i_track >= p_vcd->nb_tracks - 1 )
312                 return 0; /* EOF */
313             
314             p_area = p_input->stream.pp_areas[
315                     p_input->stream.p_selected_area->i_id + 1 ];
316             
317             msg_Dbg( p_input, "new title" );
318             
319             p_area->i_part = 1;
320             VCDSetArea( p_input, p_area );
321     
322         }
323         i_read += VCD_DATA_SIZE;
324     }
325     
326     if ( i_len % VCD_DATA_SIZE ) /* this should not happen */
327     { 
328         if ( ioctl_ReadSector( p_vcd->i_handle, p_vcd->i_sector, 
329                     p_last_sector ) < 0 )
330         {
331             msg_Err( p_input, "could not read sector %d", p_vcd->i_sector );
332             return -1;
333         }
334         
335         p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * VCD_DATA_SIZE,
336                                    p_last_sector, i_len % VCD_DATA_SIZE );
337         i_read += i_len % VCD_DATA_SIZE;
338     }
339     
340     p_input->stream.p_selected_area->i_tell = 
341         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
342          - p_input->stream.p_selected_area->i_start;
343
344     return i_read;
345 }
346
347
348 /*****************************************************************************
349  * VCDSetProgram: Does nothing since a VCD is mono_program
350  *****************************************************************************/
351 static int VCDSetProgram( input_thread_t * p_input,
352                           pgrm_descriptor_t * p_program)
353 {
354     return 0;
355 }
356
357
358 /*****************************************************************************
359  * VCDSetArea: initialize input data for title x, chapter y.
360  * It should be called for each user navigation request.
361  ****************************************************************************/
362 static int VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
363 {
364     thread_vcd_data_t *     p_vcd;
365
366     p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
367
368     /* we can't use the interface slider until initilization is complete */
369     p_input->stream.b_seekable = 0;
370
371     if( p_area != p_input->stream.p_selected_area )
372     {
373         /* Reset the Chapter position of the current title */
374         p_input->stream.p_selected_area->i_part = 1;
375         p_input->stream.p_selected_area->i_tell = 0;
376
377         /* Change the default area */
378         p_input->stream.p_selected_area = p_area;
379
380         /* Change the current track */
381         /* The first track is not a valid one  */
382         p_vcd->i_track = p_area->i_id;
383         p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track];
384     }
385
386     /* warn interface that something has changed */
387     p_input->stream.b_seekable = 1;
388     p_input->stream.b_changed = 1;
389
390     return 0;
391 }
392
393
394 /****************************************************************************
395  * VCDSeek
396  ****************************************************************************/
397 static void VCDSeek( input_thread_t * p_input, off_t i_off )
398 {
399     thread_vcd_data_t *               p_vcd;
400
401     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
402
403     p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track]
404                        + i_off / (off_t)VCD_DATA_SIZE;
405
406     p_input->stream.p_selected_area->i_tell = 
407         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
408          - p_input->stream.p_selected_area->i_start;
409 }