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