]> git.sesse.net Git - vlc/blob - plugins/dvd/dvd_access.c
*Fixed demux plugin selection.
[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.2 2002/03/06 12:26:35 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     p_input->psz_demux = "dvd";
241
242     return 0;
243 }
244
245 /*****************************************************************************
246  * DVDClose: close dvd
247  *****************************************************************************/
248 static void DVDClose( struct input_thread_s *p_input )
249 {
250     thread_dvd_data_t *     p_dvd;
251
252     p_dvd = (thread_dvd_data_t*)p_input->p_access_data;
253
254     IfoDestroy( p_dvd->p_ifo );
255
256     p_input->p_access_data = (void *)(p_dvd->dvdhandle);
257     free( p_dvd );
258
259     /* Clean up libdvdcss */
260     dvdcss_close( (dvdcss_handle) p_input->p_access_data );
261 }
262
263 /*****************************************************************************
264  * DVDSetProgram: used to change angle
265  *****************************************************************************/
266 static int DVDSetProgram( input_thread_t    * p_input,
267                           pgrm_descriptor_t * p_program ) 
268 {
269     if( p_input->stream.p_selected_program != p_program )
270     {
271         thread_dvd_data_t *  p_dvd;
272         int                  i_angle;
273     
274         p_dvd = (thread_dvd_data_t*)(p_input->p_access_data);
275         i_angle = p_program->i_number;
276
277         /* DVD is actually mono-program: we only need the current angle
278          * number, so copy the data between programs */
279         memcpy( p_program, p_input->stream.p_selected_program,
280                 sizeof(pgrm_descriptor_t) );
281         p_program->i_number = i_angle;
282         p_input->stream.p_selected_program = p_program;
283
284 #define title \
285     p_dvd->p_ifo->vts.title_unit.p_title[p_dvd->i_title_id-1].title
286         if( title.p_cell_play[p_dvd->i_prg_cell].i_category & 0xf000 )
287         {
288             if( ( p_program->i_number - p_dvd->i_angle ) < 0 )
289             {
290                 p_dvd->i_cell = 0;
291             }
292             p_dvd->i_prg_cell += ( p_program->i_number - p_dvd->i_angle );
293             p_dvd->i_angle = p_program->i_number;
294     
295             DVDFindSector( p_dvd );
296             p_dvd->i_cell += p_dvd->i_angle_cell;
297         }
298         else
299         {
300             p_dvd->i_angle = p_program->i_number;
301         }
302 #undef title
303         intf_WarnMsg( 3, "dvd info: angle %d selected", p_dvd->i_angle );
304     }
305
306     return 0;
307 }
308
309 /*****************************************************************************
310  * DVDSetArea: initialize input data for title x, chapter y.
311  * It should be called for each user navigation request.
312  *****************************************************************************
313  * Take care that i_title starts from 0 (vmg) and i_chapter start from 1.
314  * Note that you have to take the lock before entering here.
315  *****************************************************************************/
316 static int DVDSetArea( input_thread_t * p_input, input_area_t * p_area )
317 {
318     thread_dvd_data_t *  p_dvd;
319     int                  i_vts_title;
320     int                  i;
321
322     p_dvd = (thread_dvd_data_t*)(p_input->p_access_data);
323
324     /* we can't use the interface slider until initilization is complete */
325     p_input->stream.b_seekable = 0;
326
327     if( p_area != p_input->stream.p_selected_area )
328     {
329         /* Reset the Chapter position of the old title */
330         p_input->stream.p_selected_area->i_part = 0;
331         p_input->stream.p_selected_area = p_area;
332
333         /*
334          *  We have to load all title information
335          */
336         /* Change the default area */
337
338         /* title number: it is not vts nb!,
339          * it is what appears in the interface list */
340         p_dvd->i_title = p_area->i_id;
341         p_dvd->p_ifo->i_title = p_dvd->i_title;
342
343         /* set number of chapters of current title */
344         p_dvd->i_chapter_nb = p_area->i_part_nb;
345
346         /* ifo vts */
347         if( IfoTitleSet( p_dvd->p_ifo ) < 0 )
348         {
349             intf_ErrMsg( "dvd error: fatal error in vts ifo" );
350             free( p_dvd );
351             p_input->b_error = 1;
352             return -1;
353         }
354
355 #define vmg p_dvd->p_ifo->vmg
356 #define vts p_dvd->p_ifo->vts
357         /* title position inside the selected vts */
358         i_vts_title = vmg.title_inf.p_attr[p_dvd->i_title-1].i_title_num;
359         p_dvd->i_title_id =
360             vts.title_inf.p_title_start[i_vts_title-1].i_title_id;
361
362         intf_WarnMsg( 3, "dvd: title %d vts_title %d pgc %d",
363                       p_dvd->i_title, i_vts_title, p_dvd->i_title_id );
364
365    
366         /*
367          * Set selected title start and size
368          */
369         
370         /* title set offset XXX: convert to block values */
371         p_dvd->i_title_start =
372             vts.i_pos + vts.manager_inf.i_title_vob_start_sector;
373
374         /* last video cell */
375         p_dvd->i_cell = 0;
376         p_dvd->i_prg_cell = -1 +
377             vts.title_unit.p_title[p_dvd->i_title_id-1].title.i_cell_nb;
378
379         if( DVDFindCell( p_dvd ) < 0 )
380         {
381             intf_ErrMsg( "dvd error: can't find title end" );
382             p_input->b_error = 1;
383             return -1;
384         }
385         
386         /* temporary hack to fix size in some dvds */
387         if( p_dvd->i_cell >= vts.cell_inf.i_cell_nb )
388         {
389             p_dvd->i_cell = vts.cell_inf.i_cell_nb - 1;
390         }
391
392         p_dvd->i_sector = 0;
393         p_dvd->i_size = vts.cell_inf.p_cell_map[p_dvd->i_cell].i_end_sector;
394
395         if( DVDChapterSelect( p_dvd, 1 ) < 0 )
396         {
397             intf_ErrMsg( "dvd error: can't find first chapter" );
398             p_input->b_error = 1;
399             return -1;
400         }
401         
402         /* Force libdvdcss to check its title key.
403          * It is only useful for title cracking method. Methods using the
404          * decrypted disc key are fast enough to check the key at each seek */
405
406         if( dvdcss_seek( p_dvd->dvdhandle, p_dvd->i_start,
407                             DVDCSS_SEEK_KEY ) < 0 )
408         {
409             intf_ErrMsg( "dvd error: %s", dvdcss_error( p_dvd->dvdhandle ) );
410             return -1;
411         }
412
413         p_dvd->i_size -= p_dvd->i_sector + 1;
414
415         IfoPrintTitle( p_dvd );
416
417         /* Area definition */
418         p_input->stream.p_selected_area->i_start = LB2OFF( p_dvd->i_start );
419         p_input->stream.p_selected_area->i_size = LB2OFF( p_dvd->i_size );
420
421         /*
422          * Destroy obsolete ES by reinitializing programs
423          * and find all ES in title with ifo data
424          */
425         if( p_input->stream.pp_programs != NULL )
426         {
427             /* We don't use input_EndStream here since
428              * we keep area structures */
429             while( p_input->stream.i_es_number )
430             {
431                 input_DelES( p_input, p_input->stream.pp_es[0] );
432             }
433             
434             while( p_input->stream.i_pgrm_number )
435             {
436                 input_DelProgram( p_input, p_input->stream.pp_programs[0] );
437             }
438
439             if( p_input->stream.pp_selected_es )
440             {
441                 free( p_input->stream.pp_selected_es );
442                 p_input->stream.pp_selected_es = NULL;
443             }
444             p_input->stream.i_selected_es_number = 0;
445         }
446         
447         /*
448          * Angle management: angles are handled through programs
449          */
450         p_dvd->i_angle_nb = vmg.title_inf.p_attr[p_dvd->i_title-1].i_angle_nb;
451         if( ( p_dvd->i_angle <= 0 ) || p_dvd->i_angle > p_dvd->i_angle_nb )
452         {
453             p_dvd->i_angle = 1;
454         }
455  
456         input_AddProgram( p_input, 1, sizeof( stream_ps_data_t ) );
457         p_input->stream.p_selected_program = p_input->stream.pp_programs[0]; 
458
459         for( i = 1 ; i < p_dvd->i_angle_nb ; i++ )
460         {
461             input_AddProgram( p_input, i+1, 0 );
462         }
463         
464         DVDSetProgram( p_input,
465                        p_input->stream.pp_programs[p_dvd->i_angle-1] ); 
466         
467
468         /* No PSM to read in DVD mode, we already have all information */
469         p_input->stream.p_selected_program->b_is_ok = 1;
470
471         DVDReadVideo( p_input );
472         
473         DVDReadAudio( p_input );
474         
475         DVDReadSPU( p_input );
476    
477         /* FIXME: hack to check that the demuxer is ready, and set
478         * the decoders */
479         if( p_input->p_demux_module )
480         {
481             DVDLaunchDecoders( p_input );
482         }
483
484     } /* i_title >= 0 */
485     else
486     {
487         p_area = p_input->stream.p_selected_area;
488     }
489 #undef vts
490 #undef vmg
491
492     /*
493      * Chapter selection
494      */
495
496     if( p_area->i_part != p_dvd->i_chapter )
497     {
498         if( ( p_area->i_part > 0 ) &&
499             ( p_area->i_part <= p_area->i_part_nb ))
500         {
501             if( DVDChapterSelect( p_dvd, p_area->i_part ) < 0 )
502             {
503                 intf_ErrMsg( "dvd error: can't set chapter in area" );
504                 p_input->b_error = 1;
505                 return -1;
506             }
507     
508             p_input->stream.p_selected_area->i_part = p_dvd->i_chapter;
509             p_input->stream.p_selected_area->i_tell =
510                                    LB2OFF( p_dvd->i_start ) - p_area->i_start;
511     
512             intf_WarnMsg( 4, "dvd info: chapter %d start at: %lld",
513                                         p_area->i_part, p_area->i_tell );
514         }
515         else
516         {
517             p_area->i_part = 1;
518             p_dvd->i_chapter = 1;
519         }
520     }
521
522     /* warn interface that something has changed */
523     p_input->stream.b_seekable = 1;
524     p_input->stream.b_changed = 1;
525
526     return 0;
527 }
528
529 #define title \
530     p_dvd->p_ifo->vts.title_unit.p_title[p_dvd->i_title_id-1].title
531
532 /*****************************************************************************
533  * DVDRead: reads data packets.
534  *****************************************************************************
535  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
536  * bytes.
537  *****************************************************************************/
538 static int DVDRead( input_thread_t * p_input,
539                     byte_t * p_buffer, size_t i_count )
540 {
541     thread_dvd_data_t *     p_dvd;
542     int                     i_block_once;
543     int                     i_read_blocks;
544     int                     i_read_total;
545     int                     i_sector;
546     int                     i_blocks;
547     boolean_t               b_eoc;
548
549     p_dvd = (thread_dvd_data_t *)(p_input->p_access_data);
550
551     i_sector = 0;
552     i_read_total = 0;
553     i_read_blocks = 0;
554     b_eoc = 0;
555
556     i_blocks = OFF2LB(i_count);
557
558     while( i_blocks )
559     {
560         i_sector = p_dvd->i_title_start + p_dvd->i_sector;
561         i_block_once = p_dvd->i_end_sector - p_dvd->i_sector + 1;
562
563         /* Get the position of the next cell if we're at cell end */
564         if( i_block_once <= 0 )
565         {
566             int     i_angle;
567
568             p_dvd->i_cell++;
569             p_dvd->i_angle_cell++;
570
571             /* Find cell index in adress map */
572             if( DVDFindSector( p_dvd ) < 0 )
573             {
574                 intf_ErrMsg( "dvd error: can't find next cell" );
575                 return 1;
576             }
577
578             /* Position the fd pointer on the right address */
579             if( ( i_sector = dvdcss_seek( p_dvd->dvdhandle,
580                                 p_dvd->i_title_start + p_dvd->i_sector,
581                                 DVDCSS_SEEK_MPEG ) ) < 0 )
582             {
583                 intf_ErrMsg( "dvd error: %s",
584                              dvdcss_error( p_dvd->dvdhandle ) );
585                 return -1;
586             }
587
588             /* update chapter : it will be easier when we have navigation
589              * ES support */
590             if( p_dvd->i_chapter < ( p_dvd->i_chapter_nb - 1 ) )
591             {
592                 if( title.p_cell_play[p_dvd->i_prg_cell].i_category & 0xf000 )
593                 {
594                     i_angle = p_dvd->i_angle - 1;
595                 }
596                 else
597                 {
598                     i_angle = 0;
599                 }
600                 if( title.chapter_map.pi_start_cell[p_dvd->i_chapter] <=
601                     ( p_dvd->i_prg_cell - i_angle + 1 ) )
602                 {
603                     p_dvd->i_chapter++;
604                     b_eoc = 1;
605                 }
606             }
607
608             i_block_once = p_dvd->i_end_sector - p_dvd->i_sector + 1;
609         }
610
611         /* The number of blocks read is the max between the requested
612          * value and the leaving block in the cell */
613         if( i_block_once > i_blocks )
614         {
615             i_block_once = i_blocks;
616         }
617
618         /* Reads from DVD */
619         i_read_blocks = dvdcss_read( p_dvd->dvdhandle, p_buffer,
620                                      i_block_once, DVDCSS_READ_DECRYPT );
621
622         i_blocks -= i_read_blocks;
623         p_buffer += LB2OFF( i_read_blocks );
624         i_read_total += i_read_blocks;
625
626         /* Update global position */
627         p_dvd->i_sector += i_read_blocks;
628     }
629
630     vlc_mutex_lock( &p_input->stream.stream_lock );
631
632     p_input->stream.p_selected_area->i_tell =
633         LB2OFF( i_sector + i_read_total ) -
634         p_input->stream.p_selected_area->i_start;
635     if( b_eoc )
636     {
637         /* We modify i_part only at end of chapter not to erase
638          * some modification from the interface */
639         p_input->stream.p_selected_area->i_part = p_dvd->i_chapter;
640     }
641
642     if( p_input->stream.p_selected_area->i_tell
643             >= p_input->stream.p_selected_area->i_size )
644     {
645         if( ( p_dvd->i_title + 1 ) >= p_input->stream.i_area_nb )
646         {
647             /* EOF */
648             vlc_mutex_unlock( &p_input->stream.stream_lock );
649             return 0;
650         }
651
652         /* EOT */
653         intf_WarnMsg( 4, "dvd info: new title" );
654         p_dvd->i_title++;
655         DVDSetArea( p_input, p_input->stream.pp_areas[p_dvd->i_title] );
656     }
657
658     vlc_mutex_unlock( &p_input->stream.stream_lock );
659
660     return LB2OFF( i_read_total );
661 }
662
663 /*****************************************************************************
664  * DVDSeek : Goes to a given position on the stream.
665  *****************************************************************************
666  * This one is used by the input and translate chronological position from
667  * input to logical position on the device.
668  * The lock should be taken before calling this function.
669  *****************************************************************************/
670 static void DVDSeek( input_thread_t * p_input, off_t i_off )
671 {
672     thread_dvd_data_t *     p_dvd;
673     int                     i_block;
674     int                     i_prg_cell;
675     int                     i_cell;
676     int                     i_chapter;
677     int                     i_angle;
678     
679     p_dvd = ( thread_dvd_data_t * )(p_input->p_access_data);
680
681     vlc_mutex_lock( &p_input->stream.stream_lock );
682     /* we have to take care of offset of beginning of title */
683     p_dvd->i_sector = OFF2LB(i_off + p_input->stream.p_selected_area->i_start)
684                        - p_dvd->i_title_start;
685     vlc_mutex_unlock( &p_input->stream.stream_lock );
686
687     i_prg_cell = 0;
688     i_chapter = 0;
689
690     /* parse vobu address map to find program cell */
691     while( title.p_cell_play[i_prg_cell].i_end_sector < p_dvd->i_sector  )
692     {
693         i_prg_cell++;
694     }
695
696     p_dvd->i_prg_cell = i_prg_cell;
697
698     if( DVDChooseAngle( p_dvd ) < 0 )
699     {
700         p_input->b_error = 1;
701         return;        
702     }
703
704     p_dvd->i_cell = 0;
705
706     /* Find first title cell which is inside program cell */
707     if( DVDFindCell( p_dvd ) < 0 )
708     {
709         /* no following cell : we're at eof */
710         intf_ErrMsg( "dvd error: cell seeking failed" );
711         p_input->b_error = 1;
712         return;
713     }
714
715     i_cell = p_dvd->i_cell;
716
717 #define cell p_dvd->p_ifo->vts.cell_inf.p_cell_map[i_cell]
718     /* parse cell address map to find title cell containing sector */
719     while( cell.i_end_sector < p_dvd->i_sector )
720     {
721         i_cell++;
722     }
723
724     p_dvd->i_cell = i_cell;
725
726     /* if we're inside a multi-angle zone, we have to choose i_sector
727      * in the current angle ; we can't do it all the time since cells
728      * can be very wide out of such zones */
729     if( title.p_cell_play[p_dvd->i_prg_cell].i_category & 0xf000 )
730     {
731         p_dvd->i_sector = __MAX(
732                 cell.i_start_sector,
733                 title.p_cell_play[p_dvd->i_prg_cell].i_start_sector );
734     }
735
736     p_dvd->i_end_sector = __MIN(
737             cell.i_end_sector,
738             title.p_cell_play[p_dvd->i_prg_cell].i_end_sector );
739 #undef cell
740     /* update chapter */
741     if( title.p_cell_play[p_dvd->i_prg_cell].i_category & 0xf000 )
742     {
743         i_angle = p_dvd->i_angle - 1;
744     }
745     else
746     {
747         i_angle = 0;
748     }
749     if( p_dvd->i_chapter_nb > 1 )
750     {
751         while( ( title.chapter_map.pi_start_cell[i_chapter] <=
752                     ( p_dvd->i_prg_cell - i_angle + 1 ) ) &&
753                ( i_chapter < ( p_dvd->i_chapter_nb - 1 ) ) )
754         {
755             i_chapter++;
756         }
757     }
758     else
759     {
760         i_chapter = 1;
761     }
762
763     p_dvd->i_chapter = i_chapter;
764
765     if( ( i_block = dvdcss_seek( p_dvd->dvdhandle,
766                                  p_dvd->i_title_start + p_dvd->i_sector,
767                                  DVDCSS_SEEK_MPEG ) ) < 0 )
768     {
769         intf_ErrMsg( "dvd error: %s", dvdcss_error( p_dvd->dvdhandle ) );
770         p_input->b_error = 1;
771         return;
772     }
773
774     vlc_mutex_lock( &p_input->stream.stream_lock );
775     p_input->stream.p_selected_area->i_part = p_dvd->i_chapter;
776     p_input->stream.p_selected_area->i_tell =
777         LB2OFF ( i_block ) - p_input->stream.p_selected_area->i_start;
778     vlc_mutex_unlock( &p_input->stream.stream_lock );
779
780     intf_WarnMsg( 4, "Program Cell: %d Cell: %d Chapter: %d",
781                      p_dvd->i_prg_cell, p_dvd->i_cell, p_dvd->i_chapter );
782
783     return;
784 }
785
786 /*****************************************************************************
787  * DVDParse: parse command line
788  *****************************************************************************/
789 static char * DVDParse( input_thread_t * p_input )
790 {
791     thread_dvd_data_t *  p_dvd;
792     struct stat          stat_info;
793     char *               psz_parser;
794     char *               psz_device;
795     char *               psz_raw;
796     char *               psz_next;
797     boolean_t            b_options = 0;
798     int                  i_title = 1;
799     int                  i_chapter = 1;
800     int                  i_angle = 1;
801     int                  i;
802     
803     p_dvd = (thread_dvd_data_t*)(p_input->p_access_data);
804
805     psz_parser = psz_device = strdup( p_input->psz_name );
806     if( !psz_parser )
807     {
808         return NULL;
809     }
810
811     /* Parse input string :
812      * [device][@rawdevice][@[title][,[chapter][,angle]]] */
813     while( *psz_parser && *psz_parser != '@' )
814     {
815         psz_parser++;
816     }
817
818     if( *psz_parser == '@' )
819     {
820         /* Maybe found raw device or option list */
821         *psz_parser = '\0';
822         psz_raw = ++psz_parser;
823     }
824     else
825     {
826         psz_raw = NULL;
827     }
828
829     if( *psz_parser && !strtol( psz_parser, NULL, 10 ) )
830     {
831         /* what we've found is either a raw device or a partial option
832          * list e.g. @,29 or both a device and a list ; search end of string */
833         while( *psz_parser && *psz_parser != '@' )
834         {
835             psz_parser++;
836         }
837         
838         if( *psz_parser == '@' )
839         {
840             /* found end of raw device, and beginning of options */
841             *psz_parser = '\0';
842             ++psz_parser;
843             b_options = 1;
844         }
845         else
846         {
847             psz_parser = psz_raw + 1;
848             for( i=0 ; i<3 ; i++ )
849             {
850                 if( !*psz_parser )
851                 {
852                     /* we have only a raw device */
853                     break;
854                 }
855                 if( strtol( psz_parser, NULL, 10 ) )
856                 {
857                     /* we have only a partial list of options, no device */
858                     psz_parser = psz_raw;
859                     psz_raw = NULL;
860                     b_options = 1;
861                     break;
862                 }
863                 psz_parser++;
864             }
865         }
866     }
867     else
868     {
869         /* found beginning of options ; no raw device specified */
870         psz_raw = NULL;
871         b_options = 1;
872     }
873
874     if( b_options )
875     {
876         /* Found options */
877         i_title = (int)strtol( psz_parser, &psz_next, 10 );
878         if( *psz_next )
879         {
880             psz_parser = psz_next + 1;
881             i_chapter = (int)strtol( psz_parser, &psz_next, 10 );
882             if( *psz_next )
883             {
884                 i_angle = (int)strtol( psz_next + 1, NULL, 10 );
885             }
886         }
887
888         p_dvd->i_title = i_title ? i_title : 1;
889         p_dvd->i_chapter = i_chapter ? i_chapter : 1;
890         p_dvd->i_angle = i_angle ? i_angle : 1;
891     }
892
893     if( psz_raw )
894     {
895         if( *psz_raw )
896         {
897             /* check the raw device */
898             if( stat( psz_raw, &stat_info ) == -1 )
899             {
900                 intf_WarnMsg( 3, "dvd warning: cannot stat() raw"
901                                 " device `%s' (%s)",
902                              psz_raw, strerror(errno));
903                 /* put back '@' */
904                 *(psz_raw - 1) = '@';
905                 psz_raw = NULL;
906             }
907             else
908             {
909                 char * psz_env;
910                 
911 #ifndef WIN32    
912                 if( !S_ISCHR(stat_info.st_mode) )
913                 {
914                     intf_WarnMsg( 3, "dvd warning: raw device %s is"
915                                      " not a valid char device", psz_raw );
916                     /* put back '@' */
917                     *(psz_raw - 1) = '@';
918                     psz_raw = NULL;
919                 }
920                 else
921 #endif
922                 {
923                     psz_env = malloc( strlen("DVDCSS_RAW_DEVICE=")
924                                     + strlen( psz_raw ) + 1 );
925                     sprintf( psz_env, "DVDCSS_RAW_DEVICE=%s", psz_raw );
926                     putenv( psz_env );
927                 }
928             }
929         }
930         else
931         {
932             psz_raw = NULL;
933         }
934     }
935     
936     if( !*psz_device )
937     {
938         free( psz_device );
939         
940         if( !p_input->psz_access )
941         {
942             /* no device and no access specified: we probably don't want DVD */
943             return NULL;
944         }
945         psz_device = config_GetPszVariable( INPUT_DVD_DEVICE_VAR );
946     }
947
948     /* check block device */
949     if( stat( psz_device, &stat_info ) == -1 )
950     {
951         intf_ErrMsg( "input error: cannot stat() device `%s' (%s)",
952                      psz_device, strerror(errno));
953         return NULL;                    
954     }
955     
956 #ifndef WIN32    
957     if( !S_ISBLK(stat_info.st_mode) && !S_ISCHR(stat_info.st_mode) )
958     {
959         intf_WarnMsg( 3, "input: DVD plugin discarded"
960                          " (not a valid block device)" );
961         return NULL;
962     }
963 #endif
964     
965     intf_WarnMsg( 2, "input: dvd=%s raw=%s title=%d chapter=%d angle=%d",
966                   psz_device, psz_raw, p_dvd->i_title,
967                   p_dvd->i_chapter, p_dvd->i_angle );
968
969     return psz_device;
970