]> git.sesse.net Git - vlc/blob - plugins/dvd/dvd_access.c
* Modified vcd input for the new input III.
[vlc] / plugins / dvd / dvd_access.c
1 /* dvd_access.c: DVD access plugin.
2  *****************************************************************************
3  * This plugins should handle all the known specificities of the DVD format,
4  * especially the 2048 bytes logical block size.
5  * It depends on:
6  *  -libdvdcss for access and unscrambling
7  *  -dvd_ifo for ifo parsing and analyse
8  *  -dvd_udf to find files
9  *****************************************************************************
10  * Copyright (C) 1998-2001 VideoLAN
11  * $Id: dvd_access.c,v 1.1 2002/03/06 01:20:56 stef Exp $
12  *
13  * Author: Stéphane Borel <stef@via.ecp.fr>
14  *
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 2 of the License, or
18  * (at your option) any later version.
19  * 
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
28  *****************************************************************************/
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <videolan/vlc.h>
38
39 #ifdef HAVE_UNISTD_H
40 #   include <unistd.h>
41 #endif
42
43 #include <fcntl.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <string.h>
47 #include <errno.h>
48
49 #ifdef STRNCASECMP_IN_STRINGS_H
50 #   include <strings.h>
51 #endif
52
53 #ifdef GOD_DAMN_DMCA
54 #   include "dummy_dvdcss.h"
55 #else
56 #   include <videolan/dvdcss.h>
57 #endif
58
59 #include "stream_control.h"
60 #include "input_ext-intf.h"
61 #include "input_ext-dec.h"
62 #include "input_ext-plugins.h"
63
64 #include "dvd.h"
65 #include "dvd_es.h"
66 #include "dvd_seek.h"
67 #include "dvd_ifo.h"
68 #include "dvd_summary.h"
69 #include "iso_lang.h"
70
71 #include "debug.h"
72
73 /*****************************************************************************
74  * Local prototypes
75  *****************************************************************************/
76
77 /* called from outside */
78 static int  DVDOpen         ( struct input_thread_s * );
79 static void DVDClose        ( struct input_thread_s * );
80 static int  DVDSetArea      ( struct input_thread_s *, struct input_area_s * );
81 static int  DVDSetProgram   ( struct input_thread_s *, pgrm_descriptor_t * );
82 static int  DVDRead         ( struct input_thread_s *, byte_t *, size_t );
83 static void DVDSeek         ( struct input_thread_s *, off_t );
84
85 static char * DVDParse( input_thread_t * );
86
87 /*****************************************************************************
88  * Functions exported as capabilities. They are declared as static so that
89  * we don't pollute the namespace too much.
90  *****************************************************************************/
91 void _M( access_getfunctions)( function_list_t * p_function_list )
92 {
93 #define input p_function_list->functions.access
94     input.pf_open             = DVDOpen;
95     input.pf_close            = DVDClose;
96     input.pf_read             = DVDRead;
97     input.pf_set_area         = DVDSetArea;
98     input.pf_set_program      = DVDSetProgram;
99     input.pf_seek             = DVDSeek;
100 #undef input
101 }
102
103 /*
104  * Data access functions
105  */
106
107 /*****************************************************************************
108  * DVDOpen: open dvd
109  *****************************************************************************/
110 static int DVDOpen( struct input_thread_s *p_input )
111 {
112     char *               psz_device;
113     dvdcss_handle        dvdhandle;
114     thread_dvd_data_t *  p_dvd;
115     input_area_t *       p_area;
116     int                  i;
117
118     p_dvd = malloc( sizeof(thread_dvd_data_t) );
119     if( p_dvd == NULL )
120     {
121         intf_ErrMsg( "dvd error: out of memory" );
122         return -1;
123     }
124     p_input->p_access_data = (void *)p_dvd;
125     
126     /* Parse command line */
127     if( !( psz_device = DVDParse( p_input ) ) )
128     {
129         free( p_dvd );
130         return -1;
131     }
132     
133     /* 
134      * set up input
135      */ 
136     p_input->i_mtu = 0;
137
138     /*
139      *  get plugin ready
140      */ 
141     dvdhandle = dvdcss_open( psz_device );
142     
143     /* free allocated string */
144     free( psz_device );
145
146
147     if( dvdhandle == NULL )
148     {
149         intf_ErrMsg( "dvd error: dvdcss can't open device" );
150         return -1;
151     }
152
153     p_dvd->dvdhandle = (dvdcss_handle) dvdhandle;
154
155     if( dvdcss_seek( p_dvd->dvdhandle, 0, DVDCSS_NOFLAGS ) < 0 )
156     {
157         intf_ErrMsg( "dvd error: %s", dvdcss_error( p_dvd->dvdhandle ) );
158         return -1;
159     }
160
161     /* Ifo allocation & initialisation */
162     if( IfoCreate( p_dvd ) < 0 )
163     {
164         intf_ErrMsg( "dvd error: allcation error in ifo" );
165         free( p_dvd );
166         return -1;
167     }
168
169     if( IfoInit( p_dvd->p_ifo ) < 0 )
170     {
171         intf_ErrMsg( "dvd error: fatal failure in ifo" );
172         IfoDestroy( p_dvd->p_ifo );
173         free( p_dvd );
174         return -1;
175     }
176
177     /* Set stream and area data */
178     vlc_mutex_lock( &p_input->stream.stream_lock );
179
180     p_input->stream.i_method = INPUT_METHOD_DVD;
181     p_input->stream.b_pace_control = 1;
182     p_input->stream.b_seekable = 1;
183     p_input->stream.p_selected_area->i_size = 0;
184     p_input->stream.p_selected_area->i_tell = 0;
185
186     /* Initialize ES structures */
187     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
188
189 #define title_inf p_dvd->p_ifo->vmg.title_inf
190     intf_WarnMsg( 3, "dvd info: number of titles: %d", title_inf.i_title_nb );
191
192 #define area p_input->stream.pp_areas
193     /* We start from 1 here since the default area 0
194      * is reserved for video_ts.vob */
195     for( i = 1 ; i <= title_inf.i_title_nb ; i++ )
196     {
197         input_AddArea( p_input );
198
199         /* Titles are Program Chains */
200         area[i]->i_id = i;
201
202         /* Absolute start offset and size 
203          * We can only set that with vts ifo, so we do it during the
204          * first call to DVDSetArea */
205         area[i]->i_start = 0;
206         area[i]->i_size = 0;
207
208         /* Number of chapters */
209         area[i]->i_part_nb = title_inf.p_attr[i-1].i_chapter_nb;
210         area[i]->i_part = 1;
211
212         /* Offset to vts_i_0.ifo */
213         area[i]->i_plugin_data = p_dvd->p_ifo->i_start +
214                        title_inf.p_attr[i-1].i_start_sector;
215     }   
216 #undef area
217     
218     p_dvd->i_title = p_dvd->i_title <= title_inf.i_title_nb ?
219                      p_dvd->i_title : 1;
220 #undef title_inf
221
222     p_area = p_input->stream.pp_areas[p_dvd->i_title];
223     
224     p_dvd->i_chapter = p_dvd->i_chapter < p_area->i_part_nb ?
225                        p_dvd->i_chapter : 1;
226     p_area->i_part = p_dvd->i_chapter;
227     
228     p_dvd->i_audio_nb = 0;
229     p_dvd->i_spu_nb = 0;
230     
231     /* set title, chapter, audio and subpic */
232     if( DVDSetArea( p_input, p_area ) )
233     {
234         vlc_mutex_unlock( &p_input->stream.stream_lock );
235         return -1;
236     }
237
238     vlc_mutex_unlock( &p_input->stream.stream_lock );
239
240     return 0;
241 }
242
243 /*****************************************************************************
244  * DVDClose: close dvd
245  *****************************************************************************/
246 static void DVDClose( struct input_thread_s *p_input )
247 {
248     thread_dvd_data_t *     p_dvd;
249
250     p_dvd = (thread_dvd_data_t*)p_input->p_access_data;
251
252     IfoDestroy( p_dvd->p_ifo );
253
254     p_input->p_access_data = (void *)(p_dvd->dvdhandle);
255     free( p_dvd );
256
257     /* Clean up libdvdcss */
258     dvdcss_close( (dvdcss_handle) p_input->p_access_data );
259 }
260
261 /*****************************************************************************
262  * DVDSetProgram: used to change angle
263  *****************************************************************************/
264 static int DVDSetProgram( input_thread_t    * p_input,
265                           pgrm_descriptor_t * p_program ) 
266 {
267     if( p_input->stream.p_selected_program != p_program )
268     {
269         thread_dvd_data_t *  p_dvd;
270         int                  i_angle;
271     
272         p_dvd = (thread_dvd_data_t*)(p_input->p_access_data);
273         i_angle = p_program->i_number;
274
275         /* DVD is actually mono-program: we only need the current angle
276          * number, so copy the data between programs */
277         memcpy( p_program, p_input->stream.p_selected_program,
278                 sizeof(pgrm_descriptor_t) );
279         p_program->i_number = i_angle;
280         p_input->stream.p_selected_program = p_program;
281
282 #define title \
283     p_dvd->p_ifo->vts.title_unit.p_title[p_dvd->i_title_id-1].title
284         if( title.p_cell_play[p_dvd->i_prg_cell].i_category & 0xf000 )
285         {
286             if( ( p_program->i_number - p_dvd->i_angle ) < 0 )
287             {
288                 p_dvd->i_cell = 0;
289             }
290             p_dvd->i_prg_cell += ( p_program->i_number - p_dvd->i_angle );
291             p_dvd->i_angle = p_program->i_number;
292     
293             DVDFindSector( p_dvd );
294             p_dvd->i_cell += p_dvd->i_angle_cell;
295         }
296         else
297         {
298             p_dvd->i_angle = p_program->i_number;
299         }
300 #undef title
301         intf_WarnMsg( 3, "dvd info: angle %d selected", p_dvd->i_angle );
302     }
303
304     return 0;
305 }
306
307 /*****************************************************************************
308  * DVDSetArea: initialize input data for title x, chapter y.
309  * It should be called for each user navigation request.
310  *****************************************************************************
311  * Take care that i_title starts from 0 (vmg) and i_chapter start from 1.
312  * Note that you have to take the lock before entering here.
313  *****************************************************************************/
314 static int DVDSetArea( input_thread_t * p_input, input_area_t * p_area )
315 {
316     thread_dvd_data_t *  p_dvd;
317     int                  i_vts_title;
318     int                  i;
319
320     p_dvd = (thread_dvd_data_t*)(p_input->p_access_data);
321
322     /* we can't use the interface slider until initilization is complete */
323     p_input->stream.b_seekable = 0;
324
325     if( p_area != p_input->stream.p_selected_area )
326     {
327         /* Reset the Chapter position of the old title */
328         p_input->stream.p_selected_area->i_part = 0;
329         p_input->stream.p_selected_area = p_area;
330
331         /*
332          *  We have to load all title information
333          */
334         /* Change the default area */
335
336         /* title number: it is not vts nb!,
337          * it is what appears in the interface list */
338         p_dvd->i_title = p_area->i_id;
339         p_dvd->p_ifo->i_title = p_dvd->i_title;
340
341         /* set number of chapters of current title */
342         p_dvd->i_chapter_nb = p_area->i_part_nb;
343
344         /* ifo vts */
345         if( IfoTitleSet( p_dvd->p_ifo ) < 0 )
346         {
347             intf_ErrMsg( "dvd error: fatal error in vts ifo" );
348             free( p_dvd );
349             p_input->b_error = 1;
350             return -1;
351         }
352
353 #define vmg p_dvd->p_ifo->vmg
354 #define vts p_dvd->p_ifo->vts
355         /* title position inside the selected vts */
356         i_vts_title = vmg.title_inf.p_attr[p_dvd->i_title-1].i_title_num;
357         p_dvd->i_title_id =
358             vts.title_inf.p_title_start[i_vts_title-1].i_title_id;
359
360         intf_WarnMsg( 3, "dvd: title %d vts_title %d pgc %d",
361                       p_dvd->i_title, i_vts_title, p_dvd->i_title_id );
362
363    
364         /*
365          * Set selected title start and size
366          */
367         
368         /* title set offset XXX: convert to block values */
369         p_dvd->i_title_start =
370             vts.i_pos + vts.manager_inf.i_title_vob_start_sector;
371
372         /* last video cell */
373         p_dvd->i_cell = 0;
374         p_dvd->i_prg_cell = -1 +
375             vts.title_unit.p_title[p_dvd->i_title_id-1].title.i_cell_nb;
376
377         if( DVDFindCell( p_dvd ) < 0 )
378         {
379             intf_ErrMsg( "dvd error: can't find title end" );
380             p_input->b_error = 1;
381             return -1;
382         }
383         
384         /* temporary hack to fix size in some dvds */
385         if( p_dvd->i_cell >= vts.cell_inf.i_cell_nb )
386         {
387             p_dvd->i_cell = vts.cell_inf.i_cell_nb - 1;
388         }
389
390         p_dvd->i_sector = 0;
391         p_dvd->i_size = vts.cell_inf.p_cell_map[p_dvd->i_cell].i_end_sector;
392
393         if( DVDChapterSelect( p_dvd, 1 ) < 0 )
394         {
395             intf_ErrMsg( "dvd error: can't find first chapter" );
396             p_input->b_error = 1;
397             return -1;
398         }
399         
400         /* Force libdvdcss to check its title key.
401          * It is only useful for title cracking method. Methods using the
402          * decrypted disc key are fast enough to check the key at each seek */
403
404         if( dvdcss_seek( p_dvd->dvdhandle, p_dvd->i_start,
405                             DVDCSS_SEEK_KEY ) < 0 )
406         {
407             intf_ErrMsg( "dvd error: %s", dvdcss_error( p_dvd->dvdhandle ) );
408             return -1;
409         }
410
411         p_dvd->i_size -= p_dvd->i_sector + 1;
412
413         IfoPrintTitle( p_dvd );
414
415         /* Area definition */
416         p_input->stream.p_selected_area->i_start = LB2OFF( p_dvd->i_start );
417         p_input->stream.p_selected_area->i_size = LB2OFF( p_dvd->i_size );
418
419         /*
420          * Destroy obsolete ES by reinitializing programs
421          * and find all ES in title with ifo data
422          */
423         if( p_input->stream.pp_programs != NULL )
424         {
425             /* We don't use input_EndStream here since
426              * we keep area structures */
427             while( p_input->stream.i_es_number )
428             {
429                 input_DelES( p_input, p_input->stream.pp_es[0] );
430             }
431             
432             while( p_input->stream.i_pgrm_number )
433             {
434                 input_DelProgram( p_input, p_input->stream.pp_programs[0] );
435             }
436
437             if( p_input->stream.pp_selected_es )
438             {
439                 free( p_input->stream.pp_selected_es );
440                 p_input->stream.pp_selected_es = NULL;
441             }
442             p_input->stream.i_selected_es_number = 0;
443         }
444         
445         /*
446          * Angle management: angles are handled through programs
447          */
448         p_dvd->i_angle_nb = vmg.title_inf.p_attr[p_dvd->i_title-1].i_angle_nb;
449         if( ( p_dvd->i_angle <= 0 ) || p_dvd->i_angle > p_dvd->i_angle_nb )
450         {
451             p_dvd->i_angle = 1;
452         }
453  
454         input_AddProgram( p_input, 1, sizeof( stream_ps_data_t ) );
455         p_input->stream.p_selected_program = p_input->stream.pp_programs[0]; 
456
457         for( i = 1 ; i < p_dvd->i_angle_nb ; i++ )
458         {
459             input_AddProgram( p_input, i+1, 0 );
460         }
461         
462         DVDSetProgram( p_input,
463                        p_input->stream.pp_programs[p_dvd->i_angle-1] ); 
464         
465
466         /* No PSM to read in DVD mode, we already have all information */
467         p_input->stream.p_selected_program->b_is_ok = 1;
468
469         DVDReadVideo( p_input );
470         
471         DVDReadAudio( p_input );
472         
473         DVDReadSPU( p_input );
474    
475         /* FIXME: hack to check that the demuxer is ready, and set
476         * the decoders */
477         if( p_input->p_demux_module )
478         {
479             DVDLaunchDecoders( p_input );
480         }
481
482     } /* i_title >= 0 */
483     else
484     {
485         p_area = p_input->stream.p_selected_area;
486     }
487 #undef vts
488 #undef vmg
489
490     /*
491      * Chapter selection
492      */
493
494     if( p_area->i_part != p_dvd->i_chapter )
495     {
496         if( ( p_area->i_part > 0 ) &&
497             ( p_area->i_part <= p_area->i_part_nb ))
498         {
499             if( DVDChapterSelect( p_dvd, p_area->i_part ) < 0 )
500             {
501                 intf_ErrMsg( "dvd error: can't set chapter in area" );
502                 p_input->b_error = 1;
503                 return -1;
504             }
505     
506             p_input->stream.p_selected_area->i_part = p_dvd->i_chapter;
507             p_input->stream.p_selected_area->i_tell =
508                                    LB2OFF( p_dvd->i_start ) - p_area->i_start;
509     
510             intf_WarnMsg( 4, "dvd info: chapter %d start at: %lld",
511                                         p_area->i_part, p_area->i_tell );
512         }
513         else
514         {
515             p_area->i_part = 1;
516             p_dvd->i_chapter = 1;
517         }
518     }
519
520     /* warn interface that something has changed */
521     p_input->stream.b_seekable = 1;
522     p_input->stream.b_changed = 1;
523
524     return 0;
525 }
526
527 #define title \
528     p_dvd->p_ifo->vts.title_unit.p_title[p_dvd->i_title_id-1].title
529
530 /*****************************************************************************
531  * DVDRead: reads data packets.
532  *****************************************************************************
533  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
534  * bytes.
535  *****************************************************************************/
536 static int DVDRead( input_thread_t * p_input,
537                     byte_t * p_buffer, size_t i_count )
538 {
539     thread_dvd_data_t *     p_dvd;
540     int                     i_block_once;
541     int                     i_read_blocks;
542     int                     i_read_total;
543     int                     i_sector;
544     int                     i_blocks;
545     boolean_t               b_eoc;
546
547     p_dvd = (thread_dvd_data_t *)(p_input->p_access_data);
548
549     i_sector = 0;
550     i_read_total = 0;
551     i_read_blocks = 0;
552     b_eoc = 0;
553
554     i_blocks = OFF2LB(i_count);
555
556     while( i_blocks )
557     {
558         i_sector = p_dvd->i_title_start + p_dvd->i_sector;
559         i_block_once = p_dvd->i_end_sector - p_dvd->i_sector + 1;
560
561         /* Get the position of the next cell if we're at cell end */
562         if( i_block_once <= 0 )
563         {
564             int     i_angle;
565
566             p_dvd->i_cell++;
567             p_dvd->i_angle_cell++;
568
569             /* Find cell index in adress map */
570             if( DVDFindSector( p_dvd ) < 0 )
571             {
572                 intf_ErrMsg( "dvd error: can't find next cell" );
573                 return 1;
574             }
575
576             /* Position the fd pointer on the right address */
577             if( ( i_sector = dvdcss_seek( p_dvd->dvdhandle,
578                                 p_dvd->i_title_start + p_dvd->i_sector,
579                                 DVDCSS_SEEK_MPEG ) ) < 0 )
580             {
581                 intf_ErrMsg( "dvd error: %s",
582                              dvdcss_error( p_dvd->dvdhandle ) );
583                 return -1;
584             }
585
586             /* update chapter : it will be easier when we have navigation
587              * ES support */
588             if( p_dvd->i_chapter < ( p_dvd->i_chapter_nb - 1 ) )
589             {
590                 if( title.p_cell_play[p_dvd->i_prg_cell].i_category & 0xf000 )
591                 {
592                     i_angle = p_dvd->i_angle - 1;
593                 }
594                 else
595                 {
596                     i_angle = 0;
597                 }
598                 if( title.chapter_map.pi_start_cell[p_dvd->i_chapter] <=
599                     ( p_dvd->i_prg_cell - i_angle + 1 ) )
600                 {
601                     p_dvd->i_chapter++;
602                     b_eoc = 1;
603                 }
604             }
605
606             i_block_once = p_dvd->i_end_sector - p_dvd->i_sector + 1;
607         }
608
609         /* The number of blocks read is the max between the requested
610          * value and the leaving block in the cell */
611         if( i_block_once > i_blocks )
612         {
613             i_block_once = i_blocks;
614         }
615
616         /* Reads from DVD */
617         i_read_blocks = dvdcss_read( p_dvd->dvdhandle, p_buffer,
618                                      i_block_once, DVDCSS_READ_DECRYPT );
619
620         i_blocks -= i_read_blocks;
621         p_buffer += LB2OFF( i_read_blocks );
622         i_read_total += i_read_blocks;
623
624         /* Update global position */
625         p_dvd->i_sector += i_read_blocks;
626     }
627
628     vlc_mutex_lock( &p_input->stream.stream_lock );
629
630     p_input->stream.p_selected_area->i_tell =
631         LB2OFF( i_sector + i_read_total ) -
632         p_input->stream.p_selected_area->i_start;
633     if( b_eoc )
634     {
635         /* We modify i_part only at end of chapter not to erase
636          * some modification from the interface */
637         p_input->stream.p_selected_area->i_part = p_dvd->i_chapter;
638     }
639
640     if( p_input->stream.p_selected_area->i_tell
641             >= p_input->stream.p_selected_area->i_size )
642     {
643         if( ( p_dvd->i_title + 1 ) >= p_input->stream.i_area_nb )
644         {
645             /* EOF */
646             vlc_mutex_unlock( &p_input->stream.stream_lock );
647             return 0;
648         }
649
650         /* EOT */
651         intf_WarnMsg( 4, "dvd info: new title" );
652         p_dvd->i_title++;
653         DVDSetArea( p_input, p_input->stream.pp_areas[p_dvd->i_title] );
654     }
655
656     vlc_mutex_unlock( &p_input->stream.stream_lock );
657
658     return LB2OFF( i_read_total );
659 }
660
661 /*****************************************************************************
662  * DVDSeek : Goes to a given position on the stream.
663  *****************************************************************************
664  * This one is used by the input and translate chronological position from
665  * input to logical position on the device.
666  * The lock should be taken before calling this function.
667  *****************************************************************************/
668 static void DVDSeek( input_thread_t * p_input, off_t i_off )
669 {
670     thread_dvd_data_t *     p_dvd;
671     int                     i_block;
672     int                     i_prg_cell;
673     int                     i_cell;
674     int                     i_chapter;
675     int                     i_angle;
676     
677     p_dvd = ( thread_dvd_data_t * )(p_input->p_access_data);
678
679     vlc_mutex_lock( &p_input->stream.stream_lock );
680     /* we have to take care of offset of beginning of title */
681     p_dvd->i_sector = OFF2LB(i_off + p_input->stream.p_selected_area->i_start)
682                        - p_dvd->i_title_start;
683     vlc_mutex_unlock( &p_input->stream.stream_lock );
684
685     i_prg_cell = 0;
686     i_chapter = 0;
687
688     /* parse vobu address map to find program cell */
689     while( title.p_cell_play[i_prg_cell].i_end_sector < p_dvd->i_sector  )
690     {
691         i_prg_cell++;
692     }
693
694     p_dvd->i_prg_cell = i_prg_cell;
695
696     if( DVDChooseAngle( p_dvd ) < 0 )
697     {
698         p_input->b_error = 1;
699         return;        
700     }
701
702     p_dvd->i_cell = 0;
703
704     /* Find first title cell which is inside program cell */
705     if( DVDFindCell( p_dvd ) < 0 )
706     {
707         /* no following cell : we're at eof */
708         intf_ErrMsg( "dvd error: cell seeking failed" );
709         p_input->b_error = 1;
710         return;
711     }
712
713     i_cell = p_dvd->i_cell;
714
715 #define cell p_dvd->p_ifo->vts.cell_inf.p_cell_map[i_cell]
716     /* parse cell address map to find title cell containing sector */
717     while( cell.i_end_sector < p_dvd->i_sector )
718     {
719         i_cell++;
720     }
721
722     p_dvd->i_cell = i_cell;
723
724     /* if we're inside a multi-angle zone, we have to choose i_sector
725      * in the current angle ; we can't do it all the time since cells
726      * can be very wide out of such zones */
727     if( title.p_cell_play[p_dvd->i_prg_cell].i_category & 0xf000 )
728     {
729         p_dvd->i_sector = __MAX(
730                 cell.i_start_sector,
731                 title.p_cell_play[p_dvd->i_prg_cell].i_start_sector );
732     }
733
734     p_dvd->i_end_sector = __MIN(
735             cell.i_end_sector,
736             title.p_cell_play[p_dvd->i_prg_cell].i_end_sector );
737 #undef cell
738     /* update chapter */
739     if( title.p_cell_play[p_dvd->i_prg_cell].i_category & 0xf000 )
740     {
741         i_angle = p_dvd->i_angle - 1;
742     }
743     else
744     {
745         i_angle = 0;
746     }
747     if( p_dvd->i_chapter_nb > 1 )
748     {
749         while( ( title.chapter_map.pi_start_cell[i_chapter] <=
750                     ( p_dvd->i_prg_cell - i_angle + 1 ) ) &&
751                ( i_chapter < ( p_dvd->i_chapter_nb - 1 ) ) )
752         {
753             i_chapter++;
754         }
755     }
756     else
757     {
758         i_chapter = 1;
759     }
760
761     p_dvd->i_chapter = i_chapter;
762
763     if( ( i_block = dvdcss_seek( p_dvd->dvdhandle,
764                                  p_dvd->i_title_start + p_dvd->i_sector,
765                                  DVDCSS_SEEK_MPEG ) ) < 0 )
766     {
767         intf_ErrMsg( "dvd error: %s", dvdcss_error( p_dvd->dvdhandle ) );
768         p_input->b_error = 1;
769         return;
770     }
771
772     vlc_mutex_lock( &p_input->stream.stream_lock );
773     p_input->stream.p_selected_area->i_part = p_dvd->i_chapter;
774     p_input->stream.p_selected_area->i_tell =
775         LB2OFF ( i_block ) - p_input->stream.p_selected_area->i_start;
776     vlc_mutex_unlock( &p_input->stream.stream_lock );
777
778     intf_WarnMsg( 4, "Program Cell: %d Cell: %d Chapter: %d",
779                      p_dvd->i_prg_cell, p_dvd->i_cell, p_dvd->i_chapter );
780
781     return;
782 }
783
784 /*****************************************************************************
785  * DVDParse: parse command line
786  *****************************************************************************/
787 static char * DVDParse( input_thread_t * p_input )
788 {
789     thread_dvd_data_t *  p_dvd;
790     struct stat          stat_info;
791     char *               psz_parser;
792     char *               psz_device;
793     char *               psz_raw;
794     char *               psz_next;
795     boolean_t            b_options = 0;
796     int                  i_title = 1;
797     int                  i_chapter = 1;
798     int                  i_angle = 1;
799     int                  i;
800     
801     p_dvd = (thread_dvd_data_t*)(p_input->p_access_data);
802
803     psz_parser = psz_device = strdup( p_input->psz_name );
804     if( !psz_parser )
805     {
806         return NULL;
807     }
808
809     /* Parse input string :
810      * [device][@rawdevice][@[title][,[chapter][,angle]]] */
811     while( *psz_parser && *psz_parser != '@' )
812     {
813         psz_parser++;
814     }
815
816     if( *psz_parser == '@' )
817     {
818         /* Maybe found raw device or option list */
819         *psz_parser = '\0';
820         psz_raw = ++psz_parser;
821     }
822     else
823     {
824         psz_raw = NULL;
825     }
826
827     if( *psz_parser && !strtol( psz_parser, NULL, 10 ) )
828     {
829         /* what we've found is either a raw device or a partial option
830          * list e.g. @,29 or both a device and a list ; search end of string */
831         while( *psz_parser && *psz_parser != '@' )
832         {
833             psz_parser++;
834         }
835         
836         if( *psz_parser == '@' )
837         {
838             /* found end of raw device, and beginning of options */
839             *psz_parser = '\0';
840             ++psz_parser;
841             b_options = 1;
842         }
843         else
844         {
845             psz_parser = psz_raw + 1;
846             for( i=0 ; i<3 ; i++ )
847             {
848                 if( !*psz_parser )
849                 {
850                     /* we have only a raw device */
851                     break;
852                 }
853                 if( strtol( psz_parser, NULL, 10 ) )
854                 {
855                     /* we have only a partial list of options, no device */
856                     psz_parser = psz_raw;
857                     psz_raw = NULL;
858                     b_options = 1;
859                     break;
860                 }
861                 psz_parser++;
862             }
863         }
864     }
865     else
866     {
867         /* found beginning of options ; no raw device specified */
868         psz_raw = NULL;
869         b_options = 1;
870     }
871
872     if( b_options )
873     {
874         /* Found options */
875         i_title = (int)strtol( psz_parser, &psz_next, 10 );
876         if( *psz_next )
877         {
878             psz_parser = psz_next + 1;
879             i_chapter = (int)strtol( psz_parser, &psz_next, 10 );
880             if( *psz_next )
881             {
882                 i_angle = (int)strtol( psz_next + 1, NULL, 10 );
883             }
884         }
885
886         p_dvd->i_title = i_title ? i_title : 1;
887         p_dvd->i_chapter = i_chapter ? i_chapter : 1;
888         p_dvd->i_angle = i_angle ? i_angle : 1;
889     }
890
891     if( psz_raw )
892     {
893         if( *psz_raw )
894         {
895             /* check the raw device */
896             if( stat( psz_raw, &stat_info ) == -1 )
897             {
898                 intf_WarnMsg( 3, "dvd warning: cannot stat() raw"
899                                 " device `%s' (%s)",
900                              psz_raw, strerror(errno));
901                 /* put back '@' */
902                 *(psz_raw - 1) = '@';
903                 psz_raw = NULL;
904             }
905             else
906             {
907                 char * psz_env;
908                 
909 #ifndef WIN32    
910                 if( !S_ISCHR(stat_info.st_mode) )
911                 {
912                     intf_WarnMsg( 3, "dvd warning: raw device %s is"
913                                      " not a valid char device", psz_raw );
914                     /* put back '@' */
915                     *(psz_raw - 1) = '@';
916                     psz_raw = NULL;
917                 }
918                 else
919 #endif
920                 {
921                     psz_env = malloc( strlen("DVDCSS_RAW_DEVICE=")
922                                     + strlen( psz_raw ) + 1 );
923                     sprintf( psz_env, "DVDCSS_RAW_DEVICE=%s", psz_raw );
924                     putenv( psz_env );
925                 }
926             }
927         }
928         else
929         {
930             psz_raw = NULL;
931         }
932     }
933     
934     if( !*psz_device )
935     {
936         free( psz_device );
937         
938         if( !p_input->psz_access )
939         {
940             /* no device and no access specified: we probably don't want DVD */
941             return NULL;
942         }
943         psz_device = config_GetPszVariable( INPUT_DVD_DEVICE_VAR );
944     }
945
946     /* check block device */
947     if( stat( psz_device, &stat_info ) == -1 )
948     {
949         intf_ErrMsg( "input error: cannot stat() device `%s' (%s)",
950                      psz_device, strerror(errno));
951         return NULL;                    
952     }
953     
954 #ifndef WIN32    
955     if( !S_ISBLK(stat_info.st_mode) && !S_ISCHR(stat_info.st_mode) )
956     {
957         intf_WarnMsg( 3, "input: DVD plugin discarded"
958                          " (not a valid block device)" );
959         return NULL;
960     }
961 #endif
962     
963     intf_WarnMsg( 2, "input: dvd=%s raw=%s title=%d chapter=%d angle=%d",
964                   psz_device, psz_raw, p_dvd->i_title,
965                   p_dvd->i_chapter, p_dvd->i_angle );
966
967     return psz_device;
968