]> git.sesse.net Git - vlc/blob - plugins/vcd/input_vcd.c
* When reaching the end of a track, moves to the following one.
[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     thread_vcd_data_t *     p_vcd;
125     int                     i;
126     input_area_t *          p_area;
127     int                     i_title = 1;
128     int                     i_chapter = 1;
129
130     p_vcd = malloc( sizeof(thread_vcd_data_t) );
131
132     if( p_vcd == NULL )
133     {
134         intf_ErrMsg( "vcd error: out of memory" );
135         return -1;
136     }
137
138     p_input->i_mtu = VCD_DATA_ONCE;
139     p_input->p_access_data = (void *)p_vcd;
140
141     /* parse the options passed in command line : */
142     psz_orig = psz_parser = psz_source = strdup( p_input->psz_name );
143     
144     if( !psz_orig )
145     {
146         return( -1 );
147     }
148  
149     while( *psz_parser && *psz_parser != '@' )
150     {
151         psz_parser++;
152     }
153
154     if( *psz_parser == '@' )
155     {
156         /* Found options */
157         *psz_parser = '\0';
158         ++psz_parser;
159
160         i_title = (int)strtol( psz_parser, &psz_next, 10 );
161         if( *psz_next )
162         {
163             psz_parser = psz_next + 1;
164             i_chapter = (int)strtol( psz_parser, &psz_next, 10 );
165         }
166
167         i_title = i_title ? i_title : 1;
168         i_chapter = i_chapter ? i_chapter : 1;
169     }
170
171     if( !*psz_source )
172     {
173         if( !p_input->psz_access )
174         {
175             free( psz_orig );
176             return -1;
177         }
178         psz_source = config_GetPszVariable( INPUT_VCD_DEVICE_VAR );
179     }
180  
181     vlc_mutex_lock( &p_input->stream.stream_lock );
182
183     /* If we are here we can control the pace... */
184     p_input->stream.b_pace_control = 1;
185
186     p_input->stream.b_seekable = 1;
187     p_input->stream.p_selected_area->i_size = 0;
188     p_input->stream.p_selected_area->i_tell = 0;
189
190     vlc_mutex_unlock( &p_input->stream.stream_lock );
191
192     p_vcd->i_handle = open( psz_source, O_RDONLY | O_NONBLOCK );
193
194     if( p_vcd->i_handle == -1 )
195     {
196         intf_ErrMsg( "input: vcd: Could not open %s\n", psz_source );
197         free (p_vcd);
198         return -1;
199     }
200
201     /* We read the Table Of Content information */
202     p_vcd->nb_tracks = ioctl_GetTrackCount( p_vcd->i_handle,
203                                             psz_source );
204     if( p_vcd->nb_tracks < 0 )
205     {
206         intf_ErrMsg( "input: vcd: was unable to count tracks" );
207         free( p_vcd );
208         return -1;
209     }
210     else if( p_vcd->nb_tracks <= 1 )
211     {
212         intf_ErrMsg( "input: vcd: no movie tracks found" );
213         free( p_vcd );
214         return -1;
215     }
216
217     p_vcd->p_sectors = ioctl_GetSectors( p_vcd->i_handle,
218                                          psz_source );
219     if ( p_vcd->p_sectors == NULL )
220     {
221         free( p_vcd );
222         return -1;
223     }
224
225     /* Set stream and area data */
226     vlc_mutex_lock( &p_input->stream.stream_lock );
227
228     /* Initialize ES structures */
229     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
230
231     /* disc input method */
232     p_input->stream.i_method = INPUT_METHOD_VCD;
233
234 #define area p_input->stream.pp_areas
235     for( i = 1 ; i <= p_vcd->nb_tracks - 1 ; i++ )
236     {
237         input_AddArea( p_input );
238
239         /* Titles are Program Chains */
240         area[i]->i_id = i;
241
242         /* Absolute start offset and size */
243         area[i]->i_start = (off_t)p_vcd->p_sectors[i] * (off_t)VCD_DATA_SIZE;
244         area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
245                            * (off_t)VCD_DATA_SIZE;
246
247         /* Number of chapters */
248         area[i]->i_part_nb = 0;   // will be the entry points
249         area[i]->i_part = 1;
250
251         area[i]->i_plugin_data = p_vcd->p_sectors[i];
252     }
253 #undef area
254
255     p_area = p_input->stream.pp_areas[i_title];
256
257     VCDSetArea( p_input, p_area );
258
259     vlc_mutex_unlock( &p_input->stream.stream_lock );
260
261     p_input->psz_demux = "vcd";
262     
263     return 0;
264 }
265
266     
267
268 /*****************************************************************************
269  * VCDClose: closes vcd
270  *****************************************************************************/
271 static void VCDClose( struct input_thread_s *p_input )
272 {
273     thread_vcd_data_t *     p_vcd
274         = (thread_vcd_data_t *)p_input->p_access_data;
275     close( p_vcd->i_handle );
276     free( p_vcd );
277 }
278
279 /*****************************************************************************
280  * VCDRead: reads from the VCD into PES packets.
281  *****************************************************************************
282  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
283  * bytes.
284  *****************************************************************************/
285 static int VCDRead( input_thread_t * p_input, byte_t * p_buffer, 
286                      size_t i_len )
287 {
288     thread_vcd_data_t *     p_vcd;
289     int                     i_blocks;
290     int                     i_index;
291     int                     i_read;
292     byte_t                  p_last_sector[ VCD_DATA_SIZE ];
293
294     p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
295
296     i_read = 0;
297
298     /* Compute the number of blocks we have to read */
299
300     i_blocks = i_len / VCD_DATA_SIZE;
301
302     for ( i_index = 0 ; i_index < i_blocks ; i_index++ ) 
303     {
304         if ( ioctl_ReadSector( p_vcd->i_handle, p_vcd->i_sector, 
305                     p_buffer + i_index * VCD_DATA_SIZE ) < 0 )
306         {
307             intf_ErrMsg( "input: vcd: could not read sector %d\n", 
308                     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->nb_tracks - 1 )
318                 return 0; /* EOF */
319             
320             p_area = p_input->stream.pp_areas[
321                     p_input->stream.p_selected_area->i_id + 1 ];
322             
323             intf_WarnMsg( 4, "input: vcd info: new title" );
324             
325             p_area->i_part = 1;
326             VCDSetArea( p_input, p_area );
327     
328         }
329         i_read += VCD_DATA_SIZE;
330     }
331     
332     if ( i_len % VCD_DATA_SIZE ) /* this should not happen */
333     { 
334         if ( ioctl_ReadSector( p_vcd->i_handle, p_vcd->i_sector, 
335                     p_last_sector ) < 0 )
336         {
337             intf_ErrMsg( "input: vcd: could not read sector %d\n", 
338                     p_vcd->i_sector );
339             return -1;
340         }
341         
342         FAST_MEMCPY( p_buffer + i_blocks * VCD_DATA_SIZE,
343                     p_last_sector, i_len % VCD_DATA_SIZE );
344         i_read += i_len % VCD_DATA_SIZE;
345     }
346     
347     p_input->stream.p_selected_area->i_tell = 
348         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
349          - p_input->stream.p_selected_area->i_start;
350
351     return i_read;
352 }
353
354
355 /*****************************************************************************
356  * VCDSetProgram: Does nothing since a VCD is mono_program
357  *****************************************************************************/
358 static int VCDSetProgram( input_thread_t * p_input,
359                           pgrm_descriptor_t * p_program)
360 {
361     return 0;
362 }
363
364
365 /*****************************************************************************
366  * VCDSetArea: initialize input data for title x, chapter y.
367  * It should be called for each user navigation request.
368  ****************************************************************************/
369 static int VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
370 {
371     thread_vcd_data_t *     p_vcd;
372
373     p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
374
375     /* we can't use the interface slider until initilization is complete */
376     p_input->stream.b_seekable = 0;
377
378     if( p_area != p_input->stream.p_selected_area )
379     {
380         /* Reset the Chapter position of the current title */
381         p_input->stream.p_selected_area->i_part = 1;
382         p_input->stream.p_selected_area->i_tell = 0;
383
384         /* Change the default area */
385         p_input->stream.p_selected_area = p_area;
386
387         /* Change the current track */
388         /* The first track is not a valid one  */
389         p_vcd->i_track = p_area->i_id;
390         p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track];
391     }
392
393     /* warn interface that something has changed */
394     p_input->stream.b_seekable = 1;
395     p_input->stream.b_changed = 1;
396
397     return 0;
398 }
399
400
401 /*****************************************************************************
402  * VCDRewind : reads a stream backward
403  *****************************************************************************/
404 static int VCDRewind( input_thread_t * p_input )
405 {
406     return( -1 );
407 }
408
409 /****************************************************************************
410  * VCDSeek
411  ****************************************************************************/
412 static void VCDSeek( input_thread_t * p_input, off_t i_off )
413 {
414     thread_vcd_data_t *               p_vcd;
415
416     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
417
418     p_vcd->i_sector = p_vcd->p_sectors[p_vcd->i_track]
419                        + i_off / (off_t)VCD_DATA_SIZE;
420
421     p_input->stream.p_selected_area->i_tell = 
422         (off_t)p_vcd->i_sector * (off_t)VCD_DATA_SIZE
423          - p_input->stream.p_selected_area->i_start;
424 }
425
426 /*
427  * Demux functions
428  */
429
430
431 /*****************************************************************************
432  * VCDInit: initializes VCD structures
433  *****************************************************************************/
434 static int VCDInit( input_thread_t * p_input )
435 {
436     es_descriptor_t *       p_es;
437     
438     vlc_mutex_lock( &p_input->stream.stream_lock );
439     
440     /* Set program information. */
441     input_AddProgram( p_input, 0, sizeof( stream_ps_data_t ) );
442     p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
443
444     /* No PSM to read in disc mode, we already have all the information */
445     p_input->stream.p_selected_program->b_is_ok = 1;
446
447     p_es = input_AddES( p_input, p_input->stream.p_selected_program, 0xe0, 0 );
448     p_es->i_stream_id = 0xe0;
449     p_es->i_type = MPEG1_VIDEO_ES;
450     p_es->i_cat = VIDEO_ES;
451
452     if( p_main->b_video )
453     {
454         input_SelectES( p_input, p_es );
455     }
456
457     p_es = input_AddES( p_input, p_input->stream.p_selected_program, 0xc0, 0 );
458     p_es->i_stream_id = 0xc0;
459     p_es->i_type = MPEG1_AUDIO_ES;
460     p_es->b_audio = 1;
461     p_es->i_cat = AUDIO_ES;
462
463     if( p_main->b_audio )
464     {
465         input_SelectES( p_input, p_es );
466     }
467
468     vlc_mutex_unlock( &p_input->stream.stream_lock );
469     
470     return 0;
471 }
472
473 /*****************************************************************************
474  * VCDEnd: frees unused data
475  *****************************************************************************/
476 static void VCDEnd( input_thread_t * p_input )
477 {
478     thread_vcd_data_t *     p_vcd;
479
480     input_BuffersEnd( p_input->p_method_data );
481
482     p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
483
484     free( p_vcd );
485 }
486
487
488 /*****************************************************************************
489  * VCDDemux: reads and demuxes data packets
490  *****************************************************************************
491  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
492  * packets.
493  *****************************************************************************/
494 static int VCDDemux( input_thread_t * p_input )
495 {
496     int                 i;
497
498     for( i = 0; i < VCD_BLOCKS_ONCE; i++ )
499     {
500         data_packet_t *     p_data;
501         ssize_t             i_result;
502
503         i_result = input_ReadPS( p_input, &p_data );
504
505         if( i_result <= 0 )
506         {
507             return( i_result );
508         }
509
510         input_DemuxPS( p_input, p_data );
511     }
512
513     return( i );
514 }
515