]> git.sesse.net Git - vlc/blob - modules/access/dvdplay/access.c
abbd83ee984f1890888928569ef4c3eb005fdb83
[vlc] / modules / access / dvdplay / access.c
1 /*****************************************************************************
2  * access.c: access capabilities for dvdplay plugin.
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: access.c,v 1.3 2002/08/29 23:53:22 massiot Exp $
6  *
7  * Author: Stéphane Borel <stef@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdio.h>
28 #include <stdlib.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc/input.h>
32
33 #include "../../demux/mpeg/system.h"
34
35 #ifdef HAVE_UNISTD_H
36 #   include <unistd.h>
37 #endif
38
39 #include <fcntl.h>
40 #include <sys/types.h>
41 #include <string.h>
42 #include <errno.h>
43
44 #ifdef STRNCASECMP_IN_STRINGS_H
45 #   include <strings.h>
46 #endif
47
48 #if defined( WIN32 )
49 #   include <io.h>                                                 /* read() */
50 #endif
51
52 #include "dvd.h"
53 #include "es.h"
54 #include "tools.h"
55 #include "intf.h"
56
57 /*****************************************************************************
58  * Local prototypes
59  *****************************************************************************/
60 /* called from outside */
61 static int    dvdplay_SetArea       ( input_thread_t *, input_area_t * );
62 static int    dvdplay_SetProgram    ( input_thread_t *, pgrm_descriptor_t * );
63 static int    dvdplay_Read          ( input_thread_t *, byte_t *, size_t );
64 static void   dvdplay_Seek          ( input_thread_t *, off_t );
65
66 static void   pf_vmg_callback       ( void*, dvdplay_event_t );
67
68 /* only from inside */
69 static int dvdNewArea( input_thread_t *, input_area_t * );
70 static int dvdNewPGC ( input_thread_t * );
71
72 /*****************************************************************************
73  * OpenDVD: open libdvdplay
74  *****************************************************************************/
75 int E_(OpenDVD) ( vlc_object_t *p_this )
76 {
77     input_thread_t *        p_input = (input_thread_t *)p_this;
78     char *                  psz_source;
79     dvd_data_t *            p_dvd;
80     input_area_t *          p_area;
81     int                     i_title_nr;
82     int                     i_title;
83     int                     i_chapter;
84     int                     i_angle;
85     int                     i;
86     
87     p_dvd = malloc( sizeof(dvd_data_t) );
88     if( p_dvd == NULL )
89     {
90         msg_Err( p_input, "dvdplay error: out of memory" );
91         return -1;
92     }
93
94     p_input->p_access_data = (void *)p_dvd;
95
96     p_input->pf_read = dvdplay_Read;
97     p_input->pf_seek = dvdplay_Seek;
98     p_input->pf_set_area = dvdplay_SetArea;
99     p_input->pf_set_program = dvdplay_SetProgram;
100
101     /* command line */
102     if( ( psz_source = dvdplay_ParseCL( p_input, 
103                         &i_title, &i_chapter, &i_angle ) ) == NULL )
104     {
105         free( p_dvd );
106         return -1;
107     }
108
109     /* Open libdvdplay */
110     p_dvd->vmg = dvdplay_open( psz_source, pf_vmg_callback, (void*)p_input );
111
112     /* free allocated strings */
113     free( psz_source );
114
115     if( p_dvd->vmg == NULL )
116     {
117         msg_Err( p_input, "dvdplay error: can't open source" );
118         free( p_dvd );
119         return -1;
120     }
121
122     p_dvd->p_intf = NULL;
123
124     p_dvd->i_still_time = 0;
125     
126     /* set up input  */
127     p_input->i_mtu = 0;
128
129     /* Set stream and area data */
130     vlc_mutex_lock( &p_input->stream.stream_lock );
131
132     /* If we are here we can control the pace... */
133     p_input->stream.b_pace_control = 1;
134     /* seek is only allowed when we have size info */
135     p_input->stream.b_seekable = 0;
136     
137     /* Initialize ES structures */
138     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
139
140     /* disc input method */
141     p_input->stream.i_method = INPUT_METHOD_DVD;
142
143     i_title_nr = dvdplay_title_nr( p_dvd->vmg );
144 #define area p_input->stream.pp_areas
145
146     /* Area 0 for menu */
147     area[0]->i_plugin_data = 0;
148     
149     for( i = 1 ; i <= i_title_nr ; i++ )
150     {
151         input_AddArea( p_input );
152         
153         /* Titles id */
154         area[i]->i_id = i;
155
156         /* Number of chapters */
157         area[i]->i_part_nb = dvdplay_chapter_nr( p_dvd->vmg, i );
158
159         area[i]->i_plugin_data = 0;
160     }
161 #undef area
162     msg_Dbg( p_input, "number of titles: %d", i_title_nr );
163
164     i_title = i_title <= i_title_nr ? i_title : 0;
165
166     p_area = p_input->stream.pp_areas[i_title];
167     p_area->i_part = i_chapter;
168     p_input->stream.p_selected_area = NULL;
169
170     /* set title, chapter, audio and subpic */
171     if( dvdplay_SetArea( p_input, p_area ) )
172     {
173         vlc_mutex_unlock( &p_input->stream.stream_lock );
174         return -1;
175     }
176     
177     if( i_angle <= p_input->stream.i_pgrm_number )
178     {
179         dvdplay_SetProgram( p_input,
180                             p_input->stream.pp_programs[i_angle - 1] );
181     }
182
183     vlc_mutex_unlock( &p_input->stream.stream_lock );
184
185     p_input->psz_demux = "dvdplay";
186
187     return 0;
188 }
189
190 /*****************************************************************************
191  * CloseDVD: close libdvdplay
192  *****************************************************************************/
193 void E_(CloseDVD) ( vlc_object_t *p_this )
194 {
195     input_thread_t * p_input = (input_thread_t *)p_this;
196     dvd_data_t *     p_dvd = (dvd_data_t *)p_input->p_access_data;
197
198     /* close libdvdplay */
199     dvdplay_close( p_dvd->vmg );
200
201     free( p_dvd );
202     p_input->p_access_data = NULL;
203
204 }
205
206 /*****************************************************************************
207  * dvdplay_SetProgram: set dvd angle.
208  *****************************************************************************
209  * This is actually a hack to make angle change through vlc interface with
210  * no need for a specific button.
211  *****************************************************************************/
212 static int dvdplay_SetProgram( input_thread_t *     p_input,
213                                pgrm_descriptor_t *  p_program )
214 {
215     if( p_input->stream.p_selected_program != p_program )
216     {
217         dvd_data_t *    p_dvd;
218         int             i_angle;
219     
220         p_dvd = (dvd_data_t*)(p_input->p_access_data);
221         i_angle = p_program->i_number;
222
223         if( !dvdplay_angle( p_dvd->vmg, i_angle ) )
224         {
225             memcpy( p_program, p_input->stream.p_selected_program,
226                     sizeof(pgrm_descriptor_t) );
227             p_program->i_number = i_angle;
228             p_input->stream.p_selected_program = p_program;
229
230             msg_Dbg( p_input, "angle %d selected", i_angle );
231         }
232     }
233
234     return 0;
235 }
236
237 /*****************************************************************************
238  * dvdplay_SetArea: initialize input data for title x, chapter y.
239  * It should be called for each user navigation request.
240  *****************************************************************************
241  * Take care that i_title starts from 0 (vmg) and i_chapter start from 1.
242  * Note that you have to take the lock before entering here.
243  *****************************************************************************/
244 static int dvdplay_SetArea( input_thread_t * p_input, input_area_t * p_area )
245 {
246     dvd_data_t *    p_dvd;
247
248     p_dvd = (dvd_data_t*)p_input->p_access_data;
249
250     /*
251      * Title selection
252      */
253     if( p_area != p_input->stream.p_selected_area )
254     {
255         int i_chapter;
256         
257         /* prevent intf to try to seek */
258         p_input->stream.b_seekable = 0;
259         
260         /* Store selected chapter */
261         i_chapter = p_area->i_part;
262
263         dvdNewArea( p_input, p_area );
264         
265         dvdplay_start( p_dvd->vmg, p_area->i_id );
266         
267         p_area->i_part = i_chapter;
268     } /* i_title >= 0 */
269     else
270     {
271         p_area = p_input->stream.p_selected_area;
272     }
273
274     /*
275      * Chapter selection
276      */
277
278     if( p_area->i_part != dvdplay_chapter_cur( p_dvd->vmg ) )
279     {
280         if( ( p_area->i_part > 0 ) &&
281             ( p_area->i_part <= p_area->i_part_nb ))
282         {
283             dvdplay_pg( p_dvd->vmg, p_area->i_part );
284         }
285         p_area->i_part = dvdplay_chapter_cur( p_dvd->vmg );
286     }
287
288     /* warn interface that something has changed */
289     p_area->i_tell =
290         LB2OFF( dvdplay_position( p_dvd->vmg ) ) - p_area->i_start;
291     p_input->stream.b_changed = 1;
292
293     return 0;
294 }
295
296 /*****************************************************************************
297  * dvdplay_Read: reads data packets.
298  *****************************************************************************
299  * Returns -1 in case of error, the number of bytes read if everything went
300  * well.
301  *****************************************************************************/
302 static int dvdplay_Read( input_thread_t * p_input,
303                          byte_t * p_buffer, size_t i_count )
304 {
305     dvd_data_t *    p_dvd;
306     off_t           i_read;
307     
308     p_dvd = (dvd_data_t *)p_input->p_access_data;
309
310     vlc_mutex_lock( &p_input->stream.stream_lock );
311
312     i_read = LB2OFF( dvdplay_read( p_dvd->vmg, p_buffer, OFF2LB( i_count ) ) );
313     
314     p_input->stream.p_selected_area->i_tell  =
315         LB2OFF( dvdplay_position( p_dvd->vmg ) ) -
316         p_input->stream.p_selected_area->i_start;
317
318     vlc_mutex_unlock( &p_input->stream.stream_lock );
319     
320     return i_read;
321 }
322
323 /*****************************************************************************
324  * dvdplay_Seek : Goes to a given position on the stream.
325  *****************************************************************************
326  * This one is used by the input and translate chronological position from
327  * input to logical position on the device.
328  * The lock should be taken before calling this function.
329  *****************************************************************************/
330 static void dvdplay_Seek( input_thread_t * p_input, off_t i_off )
331 {
332     dvd_data_t *     p_dvd;
333     
334     p_dvd = (dvd_data_t *)p_input->p_access_data;
335
336     vlc_mutex_lock( &p_input->stream.stream_lock );
337     
338     dvdplay_seek( p_dvd->vmg, OFF2LB( i_off ) );
339
340     vlc_mutex_unlock( &p_input->stream.stream_lock );
341
342     return;
343 }
344
345
346 /*****************************************************************************
347  * pf_vmg_callback: called by libdvdplay when some event happens
348  *****************************************************************************
349  * The stream lock has to be taken before entering here
350  *****************************************************************************/
351 static void pf_vmg_callback( void* p_args, dvdplay_event_t event )
352 {
353     input_thread_t *    p_input;
354     dvd_data_t *        p_dvd;
355     int                 i;
356
357     p_input = (input_thread_t*)p_args;
358     p_dvd   = (dvd_data_t*)p_input->p_access_data;
359     
360     switch( event )
361     {
362     case NEW_DOMAIN:
363         break;
364     case NEW_VTS:
365         break;
366     case NEW_FILE:
367
368         break;
369     case NEW_PGC:
370         /* prevent intf to try to seek  by default */
371         p_input->stream.b_seekable = 0;
372
373         if( ( i = dvdplay_title_cur( p_dvd->vmg ) ) != 
374                 p_input->stream.p_selected_area->i_id )
375         {
376             /* the title number has changed: update area */
377             msg_Warn( p_input, "new title %d (%d)", i,
378                                p_input->stream.p_selected_area->i_id );
379             dvdNewArea( p_input,
380                         p_input->stream.pp_areas[i] );
381         }
382
383         /* new pgc in same title: reinit ES */
384         dvdNewPGC( p_input );
385         
386         p_input->stream.b_changed = 1;
387
388         break;
389     case NEW_PG:
390         /* update current chapter */
391         p_input->stream.p_selected_area->i_part =
392             dvdplay_chapter_cur( p_dvd->vmg );
393         break;
394     case NEW_CELL:
395         p_dvd->b_end_of_cell = 0;
396         break;
397     case END_OF_CELL:
398         p_dvd->b_end_of_cell = 1;
399         break;
400     case JUMP:
401         dvdplay_ES( p_input );
402         break;
403     case STILL_TIME:
404         /* we must pause only from demux
405          * when the data in cache has been decoded */
406         p_dvd->i_still_time = dvdplay_still_time( p_dvd->vmg );
407         msg_Dbg( p_input, "still time %d", p_dvd->i_still_time );
408         break;
409     case COMPLETE_VIDEO:
410         break;
411     case NEW_HIGHLIGHT:
412         
413         break;
414     default:
415         msg_Err( p_input, "unknown event from libdvdplay (%d)",
416                       event );
417     }
418
419     return;
420 }
421
422 static int dvdNewArea( input_thread_t * p_input, input_area_t * p_area )
423 {
424     dvd_data_t *    p_dvd;
425     int             i_angle_nb, i_angle;
426     int             i;
427
428     p_dvd = (dvd_data_t*)p_input->p_access_data;
429
430     p_input->stream.p_selected_area = p_area;
431
432     /*
433      * One program for each angle
434      */
435     while( p_input->stream.i_pgrm_number )
436     {
437         input_DelProgram( p_input, p_input->stream.pp_programs[0] );
438     }
439
440     input_AddProgram( p_input, 1, sizeof( stream_ps_data_t ) );
441     p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
442
443     dvdplay_angle_info( p_dvd->vmg, &i_angle_nb, &i_angle );
444     for( i = 1 ; i < i_angle_nb ; i++ )
445     {
446         input_AddProgram( p_input, i+1, 0 );
447     }
448     
449     dvdplay_SetProgram( p_input,
450                         p_input->stream.pp_programs[i_angle-1] ); 
451
452 //    dvdNewPGC( p_input );
453
454     /* No PSM to read in DVD mode, we already have all information */
455     p_input->stream.p_selected_program->b_is_ok = 1;
456
457     return 0;
458 }
459
460 static int dvdNewPGC( input_thread_t * p_input )
461 {
462     dvd_data_t *    p_dvd;
463 //    int             i_audio_nr  = -1;
464 //    int             i_audio     = -1;
465 //    int             i_subp_nr   = -1;
466 //    int             i_subp      = -1;
467 //    int             i_sec;
468     
469     p_dvd = (dvd_data_t*)p_input->p_access_data;
470
471 //    dvdplay_audio_info( p_dvd->vmg, &i_audio_nr, &i_audio );
472 //    dvdplay_subp_info( p_dvd->vmg, &i_subp_nr, &i_subp );
473
474     dvdplay_ES( p_input );
475     p_input->stream.p_selected_area->i_start =
476         LB2OFF( dvdplay_title_first( p_dvd->vmg ) );
477     p_input->stream.p_selected_area->i_size  =
478         LB2OFF( dvdplay_title_end ( p_dvd->vmg ) ) -
479         p_input->stream.p_selected_area->i_start;
480
481     if( p_input->stream.p_selected_area->i_size > 0 )
482     {
483         p_input->stream.b_seekable = 1;
484     }
485     else
486     {
487         p_input->stream.b_seekable = 0;
488     }
489     
490 #if 0
491     i_sec = dvdplay_title_time( p_dvd->vmg );
492     msg_Dbg( p_input, "title time: %d:%02d:%02d (%d)",
493                      i_sec/3600, (i_sec%3600)/60, i_sec%60, i_sec );
494 #endif
495
496     return 0;
497 }