a770eb7a16808bb5edf504b0b04cdcdee4042cdb
[vlc.git] / 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.12 2003/03/09 19:25:09 gbazin 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     unsigned int            i_title_nr;
82     unsigned int            i_title;
83     unsigned int            i_chapter;
84     unsigned int            i_angle;
85     unsigned int            i;
86
87     p_dvd = malloc( sizeof(dvd_data_t) );
88     if( p_dvd == NULL )
89     {
90         msg_Err( p_input, "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     if( p_dvd->vmg == NULL )
113     {
114         msg_Err( p_input, "cannot open %s", psz_source );
115         free( psz_source );
116         free( p_dvd );
117         return -1;
118     }
119
120     /* free allocated strings */
121     free( psz_source );
122
123     p_dvd->p_intf = NULL;
124
125     p_dvd->i_still_time = 0;
126
127     /* set up input  */
128     p_input->i_mtu = 0;
129
130     /* Set stream and area data */
131     vlc_mutex_lock( &p_input->stream.stream_lock );
132
133     /* If we are here we can control the pace... */
134     p_input->stream.b_pace_control = 1;
135     /* seek is only allowed when we have size info */
136     p_input->stream.b_seekable = 0;
137     p_input->stream.b_connected = 1;
138
139     /* Initialize ES structures */
140     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
141
142     /* disc input method */
143     p_input->stream.i_method = INPUT_METHOD_DVD;
144
145     i_title_nr = dvdplay_title_nr( p_dvd->vmg );
146 #define area p_input->stream.pp_areas
147
148     /* Area 0 for menu */
149     area[0]->i_plugin_data = 0;
150
151     for( i = 1 ; i <= i_title_nr ; i++ )
152     {
153         input_AddArea( p_input );
154
155         /* Titles id */
156         area[i]->i_id = i;
157
158         /* Number of chapters */
159         area[i]->i_part_nb = dvdplay_chapter_nr( p_dvd->vmg, i );
160
161         area[i]->i_plugin_data = 0;
162     }
163 #undef area
164     msg_Dbg( p_input, "number of titles: %d", i_title_nr );
165
166     i_title = i_title <= i_title_nr ? i_title : 0;
167
168     p_area = p_input->stream.pp_areas[i_title];
169     p_area->i_part = i_chapter;
170     p_input->stream.p_selected_area = NULL;
171
172     /* set title, chapter, audio and subpic */
173     if( dvdplay_SetArea( p_input, p_area ) )
174     {
175         vlc_mutex_unlock( &p_input->stream.stream_lock );
176         return -1;
177     }
178
179     if( i_angle <= p_input->stream.i_pgrm_number )
180     {
181         dvdplay_SetProgram( p_input,
182                             p_input->stream.pp_programs[i_angle - 1] );
183     }
184
185     vlc_mutex_unlock( &p_input->stream.stream_lock );
186
187     if( !p_input->psz_demux || !*p_input->psz_demux )
188     {
189         p_input->psz_demux = "dvdplay";
190     }
191
192     /* FIXME: we might lose variables here */
193     var_Create( p_input, "x-start", VLC_VAR_INTEGER );
194     var_Create( p_input, "y-start", VLC_VAR_INTEGER );
195     var_Create( p_input, "x-end", VLC_VAR_INTEGER );
196     var_Create( p_input, "y-end", VLC_VAR_INTEGER );
197
198     var_Create( p_input, "color", VLC_VAR_ADDRESS );
199     var_Create( p_input, "contrast", VLC_VAR_ADDRESS );
200
201     var_Create( p_input, "highlight", VLC_VAR_BOOL );
202     var_Create( p_input, "highlight-mutex", VLC_VAR_MUTEX );
203
204     return 0;
205 }
206
207 /*****************************************************************************
208  * CloseDVD: close libdvdplay
209  *****************************************************************************/
210 void E_(CloseDVD) ( vlc_object_t *p_this )
211 {
212     input_thread_t * p_input = (input_thread_t *)p_this;
213     dvd_data_t *     p_dvd = (dvd_data_t *)p_input->p_access_data;
214
215     var_Destroy( p_input, "highlight-mutex" );
216     var_Destroy( p_input, "highlight" );
217
218     var_Destroy( p_input, "x-start" );
219     var_Destroy( p_input, "x-end" );
220     var_Destroy( p_input, "y-start" );
221     var_Destroy( p_input, "y-end" );
222
223     var_Destroy( p_input, "color" );
224     var_Destroy( p_input, "contrast" );
225
226     /* close libdvdplay */
227     dvdplay_close( p_dvd->vmg );
228
229     free( p_dvd );
230     p_input->p_access_data = NULL;
231
232 }
233
234 /*****************************************************************************
235  * dvdplay_SetProgram: set dvd angle.
236  *****************************************************************************
237  * This is actually a hack to make angle change through vlc interface with
238  * no need for a specific button.
239  *****************************************************************************/
240 static int dvdplay_SetProgram( input_thread_t *     p_input,
241                                pgrm_descriptor_t *  p_program )
242 {
243     if( p_input->stream.p_selected_program != p_program )
244     {
245         dvd_data_t *    p_dvd;
246         int             i_angle;
247
248         p_dvd = (dvd_data_t*)(p_input->p_access_data);
249         i_angle = p_program->i_number;
250
251         if( !dvdplay_angle( p_dvd->vmg, i_angle ) )
252         {
253             memcpy( p_program, p_input->stream.p_selected_program,
254                     sizeof(pgrm_descriptor_t) );
255             p_program->i_number = i_angle;
256             p_input->stream.p_selected_program = p_program;
257
258             msg_Dbg( p_input, "angle %d selected", i_angle );
259         }
260     }
261
262     return 0;
263 }
264
265 /*****************************************************************************
266  * dvdplay_SetArea: initialize input data for title x, chapter y.
267  * It should be called for each user navigation request.
268  *****************************************************************************
269  * Take care that i_title starts from 0 (vmg) and i_chapter start from 1.
270  * Note that you have to take the lock before entering here.
271  *****************************************************************************/
272 static int dvdplay_SetArea( input_thread_t * p_input, input_area_t * p_area )
273 {
274     dvd_data_t *    p_dvd;
275
276     p_dvd = (dvd_data_t*)p_input->p_access_data;
277
278     /*
279      * Title selection
280      */
281     if( p_area != p_input->stream.p_selected_area )
282     {
283         int i_chapter;
284
285         /* prevent intf to try to seek */
286         p_input->stream.b_seekable = 0;
287
288         /* Store selected chapter */
289         i_chapter = p_area->i_part;
290
291         dvdNewArea( p_input, p_area );
292
293         /* Reinit ES */
294         dvdNewPGC( p_input );
295
296         dvdplay_start( p_dvd->vmg, p_area->i_id );
297
298         p_area->i_part = i_chapter;
299
300     } /* i_title >= 0 */
301     else
302     {
303         p_area = p_input->stream.p_selected_area;
304     }
305
306     /*
307      * Chapter selection
308      */
309
310     if( (int)p_area->i_part != dvdplay_chapter_cur( p_dvd->vmg ) )
311     {
312         if( ( p_area->i_part > 0 ) &&
313             ( p_area->i_part <= p_area->i_part_nb ))
314         {
315             dvdplay_pg( p_dvd->vmg, p_area->i_part );
316         }
317         p_area->i_part = dvdplay_chapter_cur( p_dvd->vmg );
318     }
319
320     /* warn interface that something has changed */
321     p_area->i_tell =
322         LB2OFF( dvdplay_position( p_dvd->vmg ) ) - p_area->i_start;
323     p_input->stream.b_changed = 1;
324
325     return 0;
326 }
327
328 /*****************************************************************************
329  * dvdplay_Read: reads data packets.
330  *****************************************************************************
331  * Returns -1 in case of error, the number of bytes read if everything went
332  * well.
333  *****************************************************************************/
334 static int dvdplay_Read( input_thread_t * p_input,
335                          byte_t * p_buffer, size_t i_count )
336 {
337     dvd_data_t *    p_dvd;
338     off_t           i_read;
339
340     p_dvd = (dvd_data_t *)p_input->p_access_data;
341
342     vlc_mutex_lock( &p_input->stream.stream_lock );
343     i_read = LB2OFF( dvdplay_read( p_dvd->vmg, p_buffer, OFF2LB( i_count ) ) );
344     vlc_mutex_unlock( &p_input->stream.stream_lock );
345
346     return i_read;
347 }
348
349 /*****************************************************************************
350  * dvdplay_Seek : Goes to a given position on the stream.
351  *****************************************************************************
352  * This one is used by the input and translate chronological position from
353  * input to logical position on the device.
354  * The lock should be taken before calling this function.
355  *****************************************************************************/
356 static void dvdplay_Seek( input_thread_t * p_input, off_t i_off )
357 {
358     dvd_data_t *     p_dvd;
359
360     p_dvd = (dvd_data_t *)p_input->p_access_data;
361
362     vlc_mutex_lock( &p_input->stream.stream_lock );
363
364     dvdplay_seek( p_dvd->vmg, OFF2LB( i_off ) );
365
366     p_input->stream.p_selected_area->i_tell  =
367         LB2OFF( dvdplay_position( p_dvd->vmg ) ) -
368         p_input->stream.p_selected_area->i_start;
369
370     vlc_mutex_unlock( &p_input->stream.stream_lock );
371
372     return;
373 }
374
375
376 /*****************************************************************************
377  * pf_vmg_callback: called by libdvdplay when some event happens
378  *****************************************************************************
379  * The stream lock has to be taken before entering here
380  *****************************************************************************/
381 static void pf_vmg_callback( void* p_args, dvdplay_event_t event )
382 {
383     input_thread_t *    p_input;
384     dvd_data_t *        p_dvd;
385     vlc_value_t         val;
386     unsigned int        i;
387
388     p_input = (input_thread_t*)p_args;
389     p_dvd   = (dvd_data_t*)p_input->p_access_data;
390
391     switch( event )
392     {
393     case NEW_DOMAIN:
394         break;
395     case NEW_VTS:
396         break;
397     case NEW_FILE:
398
399         break;
400     case NEW_PGC:
401         /* prevent intf to try to seek  by default */
402         p_input->stream.b_seekable = 0;
403
404         i = dvdplay_title_cur( p_dvd->vmg );
405         if( i != p_input->stream.p_selected_area->i_id )
406         {
407             /* the title number has changed: update area */
408             msg_Warn( p_input, "new title %d (%d)", i,
409                                p_input->stream.p_selected_area->i_id );
410             dvdNewArea( p_input,
411                         p_input->stream.pp_areas[i] );
412         }
413
414         /* new pgc in same title: reinit ES */
415         dvdNewPGC( p_input );
416
417         p_input->stream.b_changed = 1;
418
419         break;
420     case NEW_PG:
421         /* update current chapter */
422         p_input->stream.p_selected_area->i_part =
423             dvdplay_chapter_cur( p_dvd->vmg );
424
425         p_input->stream.p_selected_area->i_tell =
426             LB2OFF( dvdplay_position( p_dvd->vmg ) ) -
427             p_input->stream.p_selected_area->i_start;
428
429         /* warn interface that something has changed */
430         p_input->stream.b_changed = 1;
431         break;
432     case NEW_CELL:
433         p_dvd->b_end_of_cell = 0;
434         break;
435     case END_OF_CELL:
436         p_dvd->b_end_of_cell = 1;
437         break;
438     case JUMP:
439         dvdplay_ES( p_input );
440         break;
441     case STILL_TIME:
442         /* we must pause only from demux
443          * when the data in cache has been decoded */
444         p_dvd->i_still_time = dvdplay_still_time( p_dvd->vmg );
445         msg_Dbg( p_input, "still time %d", p_dvd->i_still_time );
446         break;
447     case COMPLETE_VIDEO:
448         break;
449     case NEW_HIGHLIGHT:
450         if( var_Get( p_input, "highlight-mutex", &val ) == VLC_SUCCESS )
451         {
452             vlc_mutex_t *p_mutex = val.p_address;
453             vlc_mutex_lock( p_mutex );
454
455             /* Retrieve the highlight from dvdplay */
456             dvdplay_highlight( p_dvd->vmg, &p_dvd->hli );
457
458             if( p_dvd->hli.i_x_start || p_dvd->hli.i_y_start ||
459                 p_dvd->hli.i_x_end || p_dvd->hli.i_y_end )
460             {
461                 /* Fill our internal variables with this data */
462                 val.i_int = p_dvd->hli.i_x_start;
463                 var_Set( p_input, "x-start", val );
464                 val.i_int = p_dvd->hli.i_y_start;
465                 var_Set( p_input, "y-start", val );
466                 val.i_int = p_dvd->hli.i_x_end;
467                 var_Set( p_input, "x-end", val );
468                 val.i_int = p_dvd->hli.i_y_end;
469                 var_Set( p_input, "y-end", val );
470
471                 val.p_address = (void *)p_dvd->hli.pi_color;
472                 var_Set( p_input, "color", val );
473                 val.p_address = (void *)p_dvd->hli.pi_contrast;
474                 var_Set( p_input, "contrast", val );
475
476                 /* Tell the SPU decoder that there's a new highlight */
477                 val.b_bool = VLC_TRUE;
478             }
479             else
480             {
481                 /* Turn off the highlight */
482                 val.b_bool = VLC_FALSE;
483             }
484             var_Set( p_input, "highlight", val );
485
486             vlc_mutex_unlock( p_mutex );
487         }
488         break;
489     default:
490         msg_Err( p_input, "unknown event from libdvdplay (%d)", event );
491     }
492
493     return;
494 }
495
496 static int dvdNewArea( input_thread_t * p_input, input_area_t * p_area )
497 {
498     dvd_data_t *    p_dvd;
499     int             i_angle_nb, i_angle;
500     int             i;
501
502     p_dvd = (dvd_data_t*)p_input->p_access_data;
503
504     p_input->stream.p_selected_area = p_area;
505
506     /*
507      * One program for each angle
508      */
509     while( p_input->stream.i_pgrm_number )
510     {
511         input_DelProgram( p_input, p_input->stream.pp_programs[0] );
512     }
513
514     input_AddProgram( p_input, 1, sizeof( stream_ps_data_t ) );
515     p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
516
517     dvdplay_angle_info( p_dvd->vmg, &i_angle_nb, &i_angle );
518     for( i = 1 ; i < i_angle_nb ; i++ )
519     {
520         input_AddProgram( p_input, i+1, 0 );
521     }
522
523     dvdplay_SetProgram( p_input,
524                         p_input->stream.pp_programs[i_angle-1] );
525
526     /* No PSM to read in DVD mode, we already have all information */
527     p_input->stream.p_selected_program->b_is_ok = 1;
528
529     return 0;
530 }
531
532 static int dvdNewPGC( input_thread_t * p_input )
533 {
534     dvd_data_t *    p_dvd;
535 //    int             i_audio_nr  = -1;
536 //    int             i_audio     = -1;
537 //    int             i_subp_nr   = -1;
538 //    int             i_subp      = -1;
539 //    int             i_sec;
540
541     p_dvd = (dvd_data_t*)p_input->p_access_data;
542
543 //    dvdplay_audio_info( p_dvd->vmg, &i_audio_nr, &i_audio );
544 //    dvdplay_subp_info( p_dvd->vmg, &i_subp_nr, &i_subp );
545
546     dvdplay_ES( p_input );
547     p_input->stream.p_selected_area->i_start =
548         LB2OFF( dvdplay_title_first( p_dvd->vmg ) );
549     p_input->stream.p_selected_area->i_size  =
550         LB2OFF( dvdplay_title_end ( p_dvd->vmg ) ) -
551         p_input->stream.p_selected_area->i_start;
552     p_input->stream.p_selected_area->i_tell = 0;
553
554     if( p_input->stream.p_selected_area->i_size > 0 )
555     {
556         p_input->stream.b_seekable = 1;
557     }
558     else
559     {
560         p_input->stream.b_seekable = 0;
561     }
562
563 #if 0
564     i_sec = dvdplay_title_time( p_dvd->vmg );
565     msg_Dbg( p_input, "title time: %d:%02d:%02d (%d)",
566                      i_sec/3600, (i_sec%3600)/60, i_sec%60, i_sec );
567 #endif
568
569     return 0;
570 }
571