* ./modules/access/vcd/vcd.c: changed an error to a warning in the probe
[vlc.git] / 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.8 2002/10/16 11:35:53 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         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     vlc_bool_t  b_end_of_track;           /* If the end of track was reached */
58
59 } thread_vcd_data_t;
60
61 /*****************************************************************************
62  * Local prototypes
63  *****************************************************************************/
64 static int  VCDOpen         ( vlc_object_t * );
65 static void VCDClose        ( vlc_object_t * );
66 static int  VCDRead         ( input_thread_t *, byte_t *, size_t );
67 static void VCDSeek         ( input_thread_t *, off_t );
68 static int  VCDSetArea      ( input_thread_t *, input_area_t * );
69 static int  VCDSetProgram   ( input_thread_t *, pgrm_descriptor_t * );
70
71 /*****************************************************************************
72  * Module descriptior
73  *****************************************************************************/
74 vlc_module_begin();
75     set_description( _("VCD input module") );
76     set_capability( "access", 80 );
77     set_callbacks( VCDOpen, VCDClose );
78     add_shortcut( "svcd" );
79 vlc_module_end();
80
81 /*
82  * Data reading functions
83  */
84
85 /*****************************************************************************
86  * VCDOpen: open vcd
87  *****************************************************************************/
88 static int VCDOpen( vlc_object_t *p_this )
89 {
90     input_thread_t *        p_input = (input_thread_t *)p_this;
91     char *                  psz_orig;
92     char *                  psz_parser;
93     char *                  psz_source;
94     char *                  psz_next;
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 #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     p_vcd = malloc( sizeof(thread_vcd_data_t) );
154
155     if( p_vcd == NULL )
156     {
157         msg_Err( p_input, "out of memory" );
158         free( psz_source );
159         return -1;
160     }
161     
162     p_input->p_access_data = (void *)p_vcd;
163     
164     p_input->i_mtu = VCD_DATA_ONCE;
165    
166     vlc_mutex_lock( &p_input->stream.stream_lock );
167
168     p_input->stream.b_pace_control = 1;
169
170     p_input->stream.b_seekable = 1;
171     p_input->stream.p_selected_area->i_size = 0;
172     p_input->stream.p_selected_area->i_tell = 0;
173
174     vlc_mutex_unlock( &p_input->stream.stream_lock );
175
176     if( !(p_vcd->vcddev = ioctl_Open( p_this, psz_source )) )
177     {
178         msg_Warn( p_input, "could not open %s", psz_source );
179         free( psz_source );
180         free( p_vcd );
181         return -1;
182     }
183
184     /* We read the Table Of Content information */
185     p_vcd->nb_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
186                                            p_vcd->vcddev, &p_vcd->p_sectors );
187     free( psz_source );
188     if( p_vcd->nb_tracks < 0 )
189         msg_Err( p_input, "unable to count tracks" );
190     else if( p_vcd->nb_tracks <= 1 )
191         msg_Err( p_input, "no movie tracks found" );
192     if( p_vcd->nb_tracks <= 1)
193     {
194         //input_BuffersEnd( p_input, p_input->p_method_data ); /* ??? */
195         ioctl_Close( p_this, p_vcd->vcddev );
196         free( p_vcd );
197         return -1;
198     }
199
200     /* Set stream and area data */
201     vlc_mutex_lock( &p_input->stream.stream_lock );
202
203     /* Initialize ES structures */
204     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
205
206     /* disc input method */
207     p_input->stream.i_method = INPUT_METHOD_VCD;
208
209 #define area p_input->stream.pp_areas
210     for( i = 1 ; i <= p_vcd->nb_tracks - 1 ; i++ )
211     {
212         input_AddArea( p_input );
213
214         /* Titles are Program Chains */
215         area[i]->i_id = i;
216
217         /* Absolute start offset and size */
218         area[i]->i_start = (off_t)p_vcd->p_sectors[i] * (off_t)VCD_DATA_SIZE;
219         area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
220                            * (off_t)VCD_DATA_SIZE;
221
222         /* Number of chapters */
223         area[i]->i_part_nb = 0;   /* will be the entry points */
224         area[i]->i_part = 1;
225
226         area[i]->i_plugin_data = p_vcd->p_sectors[i];
227     }
228 #undef area
229
230     p_area = p_input->stream.pp_areas[i_title];
231
232     VCDSetArea( p_input, p_area );
233
234     vlc_mutex_unlock( &p_input->stream.stream_lock );
235
236     p_input->psz_demux = "ps";
237
238     return 0;
239 }
240
241 /*****************************************************************************
242  * VCDClose: closes vcd
243  *****************************************************************************/
244 static void VCDClose( vlc_object_t *p_this )
245 {
246     input_thread_t *   p_input = (input_thread_t *)p_this;
247     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
248
249     ioctl_Close( p_this, p_vcd->vcddev );
250     free( p_vcd );
251 }
252
253 /*****************************************************************************
254  * VCDRead: reads from the VCD into PES packets.
255  *****************************************************************************
256  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
257  * bytes.
258  *****************************************************************************/
259 static int VCDRead( input_thread_t * p_input, byte_t * p_buffer, 
260                      size_t i_len )
261 {
262     thread_vcd_data_t *     p_vcd;
263     int                     i_blocks;
264     int                     i_index;
265     int                     i_read;
266     byte_t                  p_last_sector[ VCD_DATA_SIZE ];
267
268     p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
269
270     i_read = 0;
271
272     /* Compute the number of blocks we have to read */
273
274     i_blocks = i_len / VCD_DATA_SIZE;
275
276     for ( i_index = 0 ; i_index < i_blocks ; i_index++ ) 
277     {
278         if ( ioctl_ReadSector( VLC_OBJECT(p_input), p_vcd->vcddev,
279                     p_vcd->i_sector, p_buffer + i_index * VCD_DATA_SIZE ) < 0 )
280         {
281             msg_Err( p_input, "could not read sector %d", p_vcd->i_sector );
282             return -1;
283         }
284
285         p_vcd->i_sector ++;
286         if ( p_vcd->i_sector == p_vcd->p_sectors[p_vcd->i_track + 1] )
287         {
288             input_area_t *p_area;
289             
290             if ( p_vcd->i_track >= p_vcd->nb_tracks - 1 )
291                 return 0; /* EOF */
292             
293             p_area = p_input->stream.pp_areas[
294                     p_input->stream.p_selected_area->i_id + 1 ];
295             
296             msg_Dbg( p_input, "new title" );
297             
298             p_area->i_part = 1;
299             VCDSetArea( p_input, p_area );
300     
301         }
302         i_read += VCD_DATA_SIZE;
303     }
304     
305     if ( i_len % VCD_DATA_SIZE ) /* this should not happen */
306     { 
307         if ( ioctl_ReadSector( VLC_OBJECT(p_input), p_vcd->vcddev,
308                                p_vcd->i_sector, p_last_sector ) < 0 )
309         {
310             msg_Err( p_input, "could not read sector %d", p_vcd->i_sector );
311             return -1;
312         }
313         
314         p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * VCD_DATA_SIZE,
315                                    p_last_sector, i_len % VCD_DATA_SIZE );
316         i_read += i_len % VCD_DATA_SIZE;
317     }
318     
319     p_input->stream.p_selected_area->i_tell = 
320         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
321          - p_input->stream.p_selected_area->i_start;
322
323     return i_read;
324 }
325
326
327 /*****************************************************************************
328  * VCDSetProgram: Does nothing since a VCD is mono_program
329  *****************************************************************************/
330 static int VCDSetProgram( input_thread_t * p_input,
331                           pgrm_descriptor_t * p_program)
332 {
333     return 0;
334 }
335
336
337 /*****************************************************************************
338  * VCDSetArea: initialize input data for title x, chapter y.
339  * It should be called for each user navigation request.
340  ****************************************************************************/
341 static int VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
342 {
343     thread_vcd_data_t *     p_vcd;
344
345     p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
346
347     /* we can't use the interface slider until initilization is complete */
348     p_input->stream.b_seekable = 0;
349
350     if( p_area != p_input->stream.p_selected_area )
351     {
352         /* Reset the Chapter position of the current title */
353         p_input->stream.p_selected_area->i_part = 1;
354         p_input->stream.p_selected_area->i_tell = 0;
355
356         /* Change the default area */
357         p_input->stream.p_selected_area = p_area;
358
359         /* Change the current track */
360         /* The first track is not a valid one  */
361         p_vcd->i_track = p_area->i_id;
362         p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track];
363     }
364
365     /* warn interface that something has changed */
366     p_input->stream.b_seekable = 1;
367     p_input->stream.b_changed = 1;
368
369     return 0;
370 }
371
372
373 /****************************************************************************
374  * VCDSeek
375  ****************************************************************************/
376 static void VCDSeek( input_thread_t * p_input, off_t i_off )
377 {
378     thread_vcd_data_t *               p_vcd;
379
380     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
381
382     p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track]
383                        + i_off / (off_t)VCD_DATA_SIZE;
384
385     p_input->stream.p_selected_area->i_tell = 
386         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
387          - p_input->stream.p_selected_area->i_start;
388 }