]> git.sesse.net Git - vlc/blob - plugins/dvd/dvd_access.c
b6ae7a0bce48a934fa747f818e24fcfe723ea070
[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.21 2002/07/17 21:28:19 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 <vlc/vlc.h>
38 #include <vlc/input.h>
39
40 #ifdef HAVE_UNISTD_H
41 #   include <unistd.h>
42 #endif
43
44 #include <fcntl.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <string.h>
48 #include <errno.h>
49
50 #ifdef STRNCASECMP_IN_STRINGS_H
51 #   include <strings.h>
52 #endif
53
54 #ifdef GOD_DAMN_DMCA
55 #   include "dummy_dvdcss.h"
56 #else
57 #   include <dvdcss/dvdcss.h>
58 #endif
59
60 #include "dvd.h"
61 #include "dvd_es.h"
62 #include "dvd_seek.h"
63 #include "dvd_ifo.h"
64 #include "dvd_summary.h"
65 #include "iso_lang.h"
66
67 /*****************************************************************************
68  * Local prototypes
69  *****************************************************************************/
70
71 /* called from outside */
72 static int  DVDOpen         ( input_thread_t * );
73 static void DVDClose        ( input_thread_t * );
74 static int  DVDSetArea      ( input_thread_t *, input_area_t * );
75 static int  DVDSetProgram   ( input_thread_t *, pgrm_descriptor_t * );
76 static ssize_t DVDRead      ( input_thread_t *, byte_t *, size_t );
77 static void DVDSeek         ( input_thread_t *, off_t );
78
79 static char * DVDParse( input_thread_t * );
80
81 /*****************************************************************************
82  * Functions exported as capabilities. They are declared as static so that
83  * we don't pollute the namespace too much.
84  *****************************************************************************/
85 void _M( access_getfunctions)( function_list_t * p_function_list )
86 {
87 #define input p_function_list->functions.access
88     input.pf_open             = DVDOpen;
89     input.pf_close            = DVDClose;
90     input.pf_read             = DVDRead;
91     input.pf_set_area         = DVDSetArea;
92     input.pf_set_program      = DVDSetProgram;
93     input.pf_seek             = DVDSeek;
94 #undef input
95 }
96
97 /*
98  * Data access functions
99  */
100
101 #define DVDTell   LB2OFF( p_dvd->i_vts_start + p_dvd->i_vts_lb ) \
102                   - p_input->stream.p_selected_area->i_start
103
104 /*****************************************************************************
105  * DVDOpen: open dvd
106  *****************************************************************************/
107 static int DVDOpen( input_thread_t *p_input )
108 {
109     char *               psz_device;
110     thread_dvd_data_t *  p_dvd;
111     input_area_t *       p_area;
112     int                  i;
113
114     p_dvd = malloc( sizeof(thread_dvd_data_t) );
115     if( p_dvd == NULL )
116     {
117         msg_Err( p_input, "out of memory" );
118         return -1;
119     }
120     p_input->p_access_data = (void *)p_dvd;
121     
122     /* Parse command line */
123     if( !( psz_device = DVDParse( p_input ) ) )
124     {
125         free( p_dvd );
126         return -1;
127     }
128     
129     /* 
130      * set up input
131      */ 
132     p_input->i_mtu = 0;
133
134     /*
135      *  get plugin ready
136      */ 
137     p_dvd->dvdhandle = dvdcss_open( psz_device );
138     
139     /* free allocated string */
140     free( psz_device );
141
142     if( p_dvd->dvdhandle == NULL )
143     {
144         msg_Err( p_input, "dvdcss cannot open device" );
145         free( p_dvd );
146         return -1;
147     }
148
149     if( dvdcss_seek( p_dvd->dvdhandle, 0, DVDCSS_NOFLAGS ) < 0 )
150     {
151         msg_Err( p_input, "%s", dvdcss_error( p_dvd->dvdhandle ) );
152         dvdcss_close( p_dvd->dvdhandle );
153         free( p_dvd );
154         return -1;
155     }
156
157     /* Ifo allocation & initialisation */
158     if( IfoCreate( p_dvd ) < 0 )
159     {
160         msg_Err( p_input, "allcation error in ifo" );
161         dvdcss_close( p_dvd->dvdhandle );
162         free( p_dvd );
163         return -1;
164     }
165
166     if( IfoInit( p_dvd->p_ifo ) < 0 )
167     {
168         msg_Err( p_input, "fatal failure in ifo" );
169         IfoDestroy( p_dvd->p_ifo );
170         dvdcss_close( p_dvd->dvdhandle );
171         free( p_dvd );
172         return -1;
173     }
174
175     /* Set stream and area data */
176     vlc_mutex_lock( &p_input->stream.stream_lock );
177
178     p_input->stream.i_method = INPUT_METHOD_DVD;
179     p_input->stream.b_pace_control = 1;
180     p_input->stream.b_seekable = 1;
181     p_input->stream.p_selected_area->i_size = 0;
182     p_input->stream.p_selected_area->i_tell = 0;
183
184     /* Initialize ES structures */
185     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
186
187 #define title_inf p_dvd->p_ifo->vmg.title_inf
188     msg_Dbg( p_input, "number of titles: %d", title_inf.i_title_nb );
189
190 #define area p_input->stream.pp_areas
191     /* We start from 1 here since the default area 0
192      * is reserved for video_ts.vob */
193     for( i = 1 ; i <= title_inf.i_title_nb ; i++ )
194     {
195         input_AddArea( p_input );
196
197         /* Titles are Program Chains */
198         area[i]->i_id = i;
199
200         /* Absolute start offset and size 
201          * We can only set that with vts ifo, so we do it during the
202          * first call to DVDSetArea */
203         area[i]->i_start = 0;
204         area[i]->i_size = 0;
205
206         /* Number of chapters */
207         area[i]->i_part_nb = title_inf.p_attr[i-1].i_chapter_nb;
208         area[i]->i_part = 1;
209
210         /* Offset to vts_i_0.ifo */
211         area[i]->i_plugin_data = p_dvd->p_ifo->i_start +
212                        title_inf.p_attr[i-1].i_start_sector;
213     }   
214 #undef area
215     
216     p_dvd->i_title = p_dvd->i_title <= title_inf.i_title_nb ?
217                      p_dvd->i_title : 1;
218 #undef title_inf
219
220     p_area = p_input->stream.pp_areas[p_dvd->i_title];
221     
222     p_area->i_part = p_dvd->i_chapter <= p_area->i_part_nb ?
223                      p_dvd->i_chapter : 1;
224     p_dvd->i_chapter = 1;
225     
226     p_dvd->b_new_chapter = 0;
227     p_dvd->i_audio_nb = 0;
228     p_dvd->i_spu_nb = 0;
229     
230     /* set title, chapter, audio and subpic */
231     if( DVDSetArea( p_input, p_area ) < 0 )
232     {
233         vlc_mutex_unlock( &p_input->stream.stream_lock );
234         IfoDestroy( p_dvd->p_ifo );
235         dvdcss_close( p_dvd->dvdhandle );
236         free( p_dvd );
237         return -1;
238     }
239
240     vlc_mutex_unlock( &p_input->stream.stream_lock );
241
242     p_input->psz_demux = "dvdold";
243
244     return 0;
245 }
246
247 /*****************************************************************************
248  * DVDClose: close dvd
249  *****************************************************************************/
250 static void DVDClose( input_thread_t *p_input )
251 {
252     thread_dvd_data_t *p_dvd = (thread_dvd_data_t*)p_input->p_access_data;
253
254     IfoDestroy( p_dvd->p_ifo );
255     dvdcss_close( p_dvd->dvdhandle );
256     free( p_dvd );
257 }
258
259 /*****************************************************************************
260  * DVDSetProgram: used to change angle
261  *****************************************************************************/
262 static int DVDSetProgram( input_thread_t    * p_input,
263                           pgrm_descriptor_t * p_program ) 
264 {
265     if( p_input->stream.p_selected_program != p_program )
266     {
267         thread_dvd_data_t *  p_dvd;
268         int                  i_angle;
269     
270         p_dvd   = (thread_dvd_data_t*)(p_input->p_access_data);
271         i_angle = p_program->i_number;
272
273         /* DVD is actually mono-program: we only need the current angle
274          * number, so copy the data between programs */
275         memcpy( p_program,
276                 p_input->stream.p_selected_program,
277                 sizeof(pgrm_descriptor_t) );
278         p_program->i_number                = i_angle;
279         p_input->stream.p_selected_program = p_program;
280
281 #define title \
282     p_dvd->p_ifo->vts.title_unit.p_title[p_dvd->i_title_id-1].title
283         if( title.p_cell_play[p_dvd->i_prg_cell].i_category & 0xf000 )
284         {
285             if( ( p_program->i_number - p_dvd->i_angle ) < 0 )
286             {
287                 /* we have to go backwards */
288                 p_dvd->i_map_cell = 0;
289             }
290             p_dvd->i_prg_cell += ( p_program->i_number - p_dvd->i_angle );
291             p_dvd->i_map_cell =  CellPrg2Map( p_dvd );
292             p_dvd->i_map_cell += p_dvd->i_angle_cell;
293             p_dvd->i_vts_lb   =  CellFirstSector( p_dvd );
294             p_dvd->i_last_lb  =  CellLastSector( p_dvd );
295             p_dvd->i_angle    =  p_program->i_number;
296         }
297         else
298         {
299             p_dvd->i_angle    =  p_program->i_number;
300         }
301 #undef title
302         msg_Dbg( p_input, "angle %d selected", p_dvd->i_angle );
303     }
304
305     return 0;
306 }
307
308 /*****************************************************************************
309  * DVDSetArea: initialize input data for title x, chapter y.
310  * It should be called for each user navigation request.
311  *****************************************************************************
312  * Take care that i_title starts from 0 (vmg) and i_chapter start from 1.
313  * Note that you have to take the lock before entering here.
314  *****************************************************************************/
315 #define vmg p_dvd->p_ifo->vmg
316 #define vts p_dvd->p_ifo->vts
317
318 static void DVDFlushStream( input_thread_t * p_input )
319 {
320     if( p_input->stream.pp_programs != NULL )
321     {
322         /* We don't use input_EndStream here since
323          * we keep area structures */
324         while( p_input->stream.i_es_number )
325         {
326             input_DelES( p_input, p_input->stream.pp_es[0] );
327         }
328         
329         while( p_input->stream.i_pgrm_number )
330         {
331             input_DelProgram( p_input, p_input->stream.pp_programs[0] );
332         }
333
334         if( p_input->stream.pp_selected_es )
335         {
336             free( p_input->stream.pp_selected_es );
337             p_input->stream.pp_selected_es = NULL;
338         }
339         p_input->stream.i_selected_es_number = 0;
340     }
341
342     return;
343 }
344
345 static int DVDReadAngle( input_thread_t * p_input )
346 {
347     thread_dvd_data_t * p_dvd;
348     int                 i_angle_nb;
349     int                 i;
350
351     p_dvd      = (thread_dvd_data_t*)(p_input->p_access_data);
352     i_angle_nb = vmg.title_inf.p_attr[p_dvd->i_title-1].i_angle_nb;
353     
354     input_AddProgram( p_input, 1, sizeof( stream_ps_data_t ) );
355     p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
356
357     for( i = 1 ; i < i_angle_nb ; i++ )
358     {
359         input_AddProgram( p_input, i+1, 0 );
360     }
361
362     return i_angle_nb;
363 }
364
365 static int DVDSetArea( input_thread_t * p_input, input_area_t * p_area )
366 {
367     thread_dvd_data_t *  p_dvd;
368
369     p_dvd = (thread_dvd_data_t*)(p_input->p_access_data);
370
371     /* we can't use the interface slider until initilization is complete */
372     p_input->stream.b_seekable = 0;
373
374     if( p_area != p_input->stream.p_selected_area )
375     {
376         int     i_vts_title;
377         u32     i_first;
378         u32     i_last;
379
380         /* Reset the Chapter position of the old title */
381         p_input->stream.p_selected_area->i_part = 1;
382         p_input->stream.p_selected_area         = p_area;
383
384         /*
385          *  We have to load all title information
386          */
387
388         /* title number as it appears in the interface list */
389         p_dvd->i_title      = p_area->i_id;
390         p_dvd->i_chapter_nb = p_area->i_part_nb;
391
392         if( IfoTitleSet( p_dvd->p_ifo, p_dvd->i_title ) < 0 )
393         {
394             msg_Err( p_input, "fatal error in vts ifo" );
395             free( p_dvd );
396             return -1;
397         }
398
399         /* title position inside the selected vts */
400         i_vts_title       = vmg.title_inf.p_attr[p_dvd->i_title-1].i_title_num;
401         p_dvd->i_title_id =
402             vts.title_inf.p_title_start[i_vts_title-1].i_title_id;
403
404         msg_Dbg( p_input, "title %d vts_title %d pgc %d",
405                           p_dvd->i_title, i_vts_title, p_dvd->i_title_id );
406
407         /* title set offset XXX: convert to block values */
408         p_dvd->i_vts_start =
409             vts.i_pos + vts.manager_inf.i_title_vob_start_sector;
410
411         /* last cell */
412         p_dvd->i_prg_cell = -1 +
413             vts.title_unit.p_title[p_dvd->i_title_id-1].title.i_cell_nb;
414         p_dvd->i_map_cell = 0;
415         p_dvd->i_map_cell = CellPrg2Map( p_dvd );
416         i_last            = CellLastSector( p_dvd );
417
418         /* first cell */
419         p_dvd->i_prg_cell   = 0;
420         p_dvd->i_map_cell   = 0;
421         p_dvd->i_angle_cell = 0;
422         p_dvd->i_map_cell   = CellPrg2Map    ( p_dvd );
423         p_dvd->i_vts_lb     = CellFirstSector( p_dvd );
424         p_dvd->i_last_lb    = CellLastSector ( p_dvd );
425
426         /* Force libdvdcss to check its title key.
427          * It is only useful for title cracking method. Methods using the
428          * decrypted disc key are fast enough to check the key at each seek */
429         i_first = dvdcss_seek( p_dvd->dvdhandle,
430                                p_dvd->i_vts_start + p_dvd->i_vts_lb,
431                                DVDCSS_SEEK_KEY );
432         if( i_first < 0 )
433         {
434             msg_Err( p_input, "%s", dvdcss_error( p_dvd->dvdhandle ) );
435             return -1;
436         }
437
438         /* Area definition */
439         p_input->stream.p_selected_area->i_start = LB2OFF( i_first );
440         p_input->stream.p_selected_area->i_size  =
441                                         LB2OFF( i_last + 1 - p_dvd->i_vts_lb );
442
443         /* Destroy obsolete ES by reinitializing programs */
444         DVDFlushStream( p_input );
445         
446         /* Angle management: angles are handled through programs */
447         p_dvd->i_angle_nb = DVDReadAngle( p_input );
448         if( ( p_dvd->i_angle <= 0 ) || p_dvd->i_angle > p_dvd->i_angle_nb )
449         {
450             p_dvd->i_angle = 1;
451         }
452        
453         DVDSetProgram( p_input,
454                        p_input->stream.pp_programs[p_dvd->i_angle-1] ); 
455
456         msg_Dbg( p_input, "title first %i, last %i, size %i",
457                           i_first, i_last, i_last + 1 - p_dvd->i_vts_lb );
458         IfoPrintTitle( p_dvd );
459
460         /* No PSM to read in DVD mode, we already have all information */
461         p_input->stream.p_selected_program->b_is_ok = 1;
462
463         /* Find all ES in title with ifo data */
464         DVDReadVideo( p_input );
465         DVDReadAudio( p_input );
466         DVDReadSPU  ( p_input );
467    
468         if( p_input->p_demux_module )
469         {
470             DVDLaunchDecoders( p_input );
471         }
472
473     } /* i_title >= 0 */
474     else
475     {
476         p_area = p_input->stream.p_selected_area;
477     }
478
479     /* Chapter selection */
480     p_dvd->i_chapter = DVDSetChapter( p_dvd, p_area->i_part );
481     
482     p_input->stream.p_selected_area->i_tell = DVDTell;
483
484     /* warn interface that something has changed */
485     p_input->stream.b_seekable = 1;
486     p_input->stream.b_changed  = 1;
487
488     return 0;
489 }
490 #undef vts
491 #undef vmg
492
493 #define title \
494     p_dvd->p_ifo->vts.title_unit.p_title[p_dvd->i_title_id-1].title
495     
496 /*****************************************************************************
497  * DVDRead: reads data packets.
498  *****************************************************************************
499  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
500  * bytes.
501  *****************************************************************************/
502 static ssize_t DVDRead( input_thread_t * p_input,
503                         byte_t * p_buffer, size_t i_count )
504 {
505     thread_dvd_data_t *     p_dvd;
506     int                     i_read;
507     int                     i_blocks;
508     int                     i_block_once = 0;
509
510     p_dvd = (thread_dvd_data_t *)(p_input->p_access_data);
511
512     i_read = 0;
513     i_blocks = OFF2LB(i_count);
514
515     while( i_blocks )
516     {
517         i_block_once = LbMaxOnce( p_dvd );
518         if( i_block_once > i_blocks )
519         {
520             i_block_once = i_blocks;
521         }
522         else if( i_block_once <= 0 )
523         {
524             /* EOT */
525             break;
526         }
527
528         if( i_block_once != dvdcss_read( p_dvd->dvdhandle, p_buffer,
529                                          i_block_once, DVDCSS_READ_DECRYPT ) )
530         {
531             return -1;
532         }
533
534         i_blocks -= i_block_once;
535         i_read += i_block_once;
536         p_buffer += LB2OFF( i_block_once );
537
538         /* Update global position */
539         p_dvd->i_vts_lb += i_block_once;
540     }
541
542     vlc_mutex_lock( &p_input->stream.stream_lock );
543
544     p_input->stream.p_selected_area->i_tell += LB2OFF( i_read );
545     if( p_dvd->b_new_chapter )
546     {
547         p_input->stream.p_selected_area->i_part = p_dvd->i_chapter;
548         p_dvd->b_new_chapter                    = 0;
549     }
550
551     if( ( p_input->stream.p_selected_area->i_tell
552             >= p_input->stream.p_selected_area->i_size )
553        || ( i_block_once  <= 0 ) )
554     {
555         if( ( p_dvd->i_title + 1 ) >= p_input->stream.i_area_nb )
556         {
557             /* EOF */
558             vlc_mutex_unlock( &p_input->stream.stream_lock );
559             return 0;
560         }
561
562         /* EOT */
563         msg_Dbg( p_input, "new title" );
564         p_dvd->i_title++;
565         DVDSetArea( p_input, p_input->stream.pp_areas[p_dvd->i_title] );
566     }
567
568     vlc_mutex_unlock( &p_input->stream.stream_lock );
569
570     return LB2OFF( i_read );
571 }
572
573 /*****************************************************************************
574  * DVDSeek : Goes to a given position on the stream.
575  *****************************************************************************
576  * This one is used by the input and translate chronological position from
577  * input to logical position on the device.
578  * The lock should be taken before calling this function.
579  *****************************************************************************/
580 static void DVDSeek( input_thread_t * p_input, off_t i_off )
581 {
582     thread_dvd_data_t *     p_dvd;
583     
584     p_dvd = ( thread_dvd_data_t * )(p_input->p_access_data);
585
586     vlc_mutex_lock( &p_input->stream.stream_lock );
587     p_dvd->i_vts_lb = OFF2LB(i_off + p_input->stream.p_selected_area->i_start)
588                        - p_dvd->i_vts_start;
589     vlc_mutex_unlock( &p_input->stream.stream_lock );
590
591     p_dvd->i_prg_cell = Lb2CellPrg( p_dvd );
592     p_dvd->i_map_cell = Lb2CellMap( p_dvd );
593     
594     if( CellIsInterleaved( p_dvd ) )
595     {
596         /* if we're inside a multi-angle zone, we have to choose i_sector
597          * in the current angle ; we can't do it all the time since cells
598          * can be very wide out of such zones */
599         p_dvd->i_vts_lb = CellFirstSector( p_dvd );
600     }
601     
602     p_dvd->i_last_lb  = CellLastSector( p_dvd );
603     p_dvd->i_chapter  = CellPrg2Chapter( p_dvd );
604
605     if( dvdcss_seek( p_dvd->dvdhandle, p_dvd->i_vts_start + p_dvd->i_vts_lb,
606                      DVDCSS_SEEK_MPEG ) < 0 )
607     {
608         msg_Err( p_input, "%s", dvdcss_error( p_dvd->dvdhandle ) );
609         p_input->b_error = 1;
610         return;
611     }
612
613     vlc_mutex_lock( &p_input->stream.stream_lock );
614     p_input->stream.p_selected_area->i_part = p_dvd->i_chapter;
615     p_input->stream.p_selected_area->i_tell = DVDTell;
616     vlc_mutex_unlock( &p_input->stream.stream_lock );
617
618     msg_Dbg( p_input, "program cell: %d cell: %d chapter: %d tell %lld",
619              p_dvd->i_prg_cell, p_dvd->i_map_cell, p_dvd->i_chapter, DVDTell );
620
621     return;
622 }
623
624 /*****************************************************************************
625  * DVDParse: parse command line
626  *****************************************************************************/
627 static char * DVDParse( input_thread_t * p_input )
628 {
629     thread_dvd_data_t *  p_dvd;
630     struct stat          stat_info;
631     char *               psz_parser;
632     char *               psz_device;
633     char *               psz_raw;
634     char *               psz_next;
635     vlc_bool_t           b_options = 0;
636     int                  i_title = 1;
637     int                  i_chapter = 1;
638     int                  i_angle = 1;
639     int                  i;
640
641     p_dvd = (thread_dvd_data_t*)(p_input->p_access_data);
642
643 #ifdef WIN32
644     /* On Win32 we want the DVD access plugin to be explicitly requested,
645      * we end up with lots of problems otherwise */
646     if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
647 #endif
648
649     psz_parser = psz_device = strdup( p_input->psz_name );
650     if( !psz_parser )
651     {
652         return NULL;
653     }
654
655     /* Parse input string :
656      * [device][@rawdevice][@[title][,[chapter][,angle]]] */
657     while( *psz_parser && *psz_parser != '@' )
658     {
659         psz_parser++;
660     }
661
662     if( *psz_parser == '@' )
663     {
664         /* Maybe found raw device or option list */
665         *psz_parser = '\0';
666         psz_raw = ++psz_parser;
667     }
668     else
669     {
670         psz_raw = "";
671     }
672
673     if( *psz_parser && !strtol( psz_parser, NULL, 10 ) )
674     {
675         /* what we've found is either a raw device or a partial option
676          * list e.g. @,29 or both a device and a list ; search end of string */
677         while( *psz_parser && *psz_parser != '@' )
678         {
679             psz_parser++;
680         }
681         
682         if( *psz_parser == '@' )
683         {
684             /* found end of raw device, and beginning of options */
685             *psz_parser = '\0';
686             ++psz_parser;
687             b_options = 1;
688         }
689         else
690         {
691             psz_parser = psz_raw + 1;
692             for( i=0 ; i<3 ; i++ )
693             {
694                 if( !*psz_parser )
695                 {
696                     /* we have only a raw device */
697                     break;
698                 }
699                 if( strtol( psz_parser, NULL, 10 ) )
700                 {
701                     /* we have only a partial list of options, no device */
702                     psz_parser = psz_raw;
703                     psz_raw = "";
704                     b_options = 1;
705                     break;
706                 }
707                 psz_parser++;
708             }
709         }
710     }
711     else
712     {
713         /* found beginning of options ; no raw device specified */
714         psz_raw = "";
715         b_options = 1;
716     }
717
718     if( b_options )
719     {
720         /* Found options */
721         i_title = (int)strtol( psz_parser, &psz_next, 10 );
722         if( *psz_next )
723         {
724             psz_parser = psz_next + 1;
725             i_chapter = (int)strtol( psz_parser, &psz_next, 10 );
726             if( *psz_next )
727             {
728                 i_angle = (int)strtol( psz_next + 1, NULL, 10 );
729             }
730         }
731
732         p_dvd->i_title = i_title ? i_title : 1;
733         p_dvd->i_chapter = i_chapter ? i_chapter : 1;
734         p_dvd->i_angle = i_angle ? i_angle : 1;
735     }
736
737     if( *psz_raw )
738     {
739         if( *psz_raw )
740         {
741             /* check the raw device */
742             if( stat( psz_raw, &stat_info ) == -1 )
743             {
744                 msg_Warn( p_input, "cannot stat() raw device `%s' (%s)",
745                                    psz_raw, strerror(errno));
746                 /* put back '@' */
747                 *(psz_raw - 1) = '@';
748                 psz_raw = "";
749             }
750             else
751             {
752                 char * psz_env;
753                 
754 #ifndef WIN32    
755                 if( !S_ISCHR(stat_info.st_mode) )
756                 {
757                     msg_Warn( p_input, "raw device %s is"
758                               " not a valid char device", psz_raw );
759                     /* put back '@' */
760                     *(psz_raw - 1) = '@';
761                     psz_raw = "";
762                 }
763                 else
764 #endif
765                 {
766                     psz_env = malloc( strlen("DVDCSS_RAW_DEVICE=")
767                                     + strlen( psz_raw ) + 1 );
768                     sprintf( psz_env, "DVDCSS_RAW_DEVICE=%s", psz_raw );
769                     putenv( psz_env );
770                 }
771             }
772         }
773         else
774         {
775             psz_raw = "";
776         }
777     }
778     
779     if( !*psz_device )
780     {
781         free( psz_device );
782         
783         if( !p_input->psz_access )
784         {
785             /* no device and no access specified: we probably don't want DVD */
786             return NULL;
787         }
788         psz_device = config_GetPsz( p_input, "dvd" );
789     }
790
791 #ifndef WIN32    
792     /* check block device */
793     if( stat( psz_device, &stat_info ) == -1 )
794     {
795         msg_Err( p_input, "cannot stat() device `%s' (%s)",
796                           psz_device, strerror(errno));
797         free( psz_device );
798         return NULL;                    
799     }
800     
801     if( !S_ISBLK(stat_info.st_mode) && !S_ISCHR(stat_info.st_mode) )
802     {
803         msg_Warn( p_input,
804                   "dvd module discarded (not a valid block device)" );
805         free( psz_device );
806         return NULL;
807     }
808 #endif
809     
810     msg_Dbg( p_input, "dvd=%s raw=%s title=%d chapter=%d angle=%d",
811                       psz_device, psz_raw, p_dvd->i_title,
812                       p_dvd->i_chapter, p_dvd->i_angle );
813
814     return psz_device;
815