]> git.sesse.net Git - vlc/blob - modules/access/dvdplay/access.c
* ./include/interface.h: grmbl, I forgot VLC_EXPORT did not support multiline
[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.11 2003/01/29 11:17:44 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         dvdplay_start( p_dvd->vmg, p_area->i_id );
294
295         p_area->i_part = i_chapter;
296     } /* i_title >= 0 */
297     else
298     {
299         p_area = p_input->stream.p_selected_area;
300     }
301
302     /*
303      * Chapter selection
304      */
305
306     if( (int)p_area->i_part != dvdplay_chapter_cur( p_dvd->vmg ) )
307     {
308         if( ( p_area->i_part > 0 ) &&
309             ( p_area->i_part <= p_area->i_part_nb ))
310         {
311             dvdplay_pg( p_dvd->vmg, p_area->i_part );
312         }
313         p_area->i_part = dvdplay_chapter_cur( p_dvd->vmg );
314     }
315
316     /* warn interface that something has changed */
317     p_area->i_tell =
318         LB2OFF( dvdplay_position( p_dvd->vmg ) ) - p_area->i_start;
319     p_input->stream.b_changed = 1;
320
321     return 0;
322 }
323
324 /*****************************************************************************
325  * dvdplay_Read: reads data packets.
326  *****************************************************************************
327  * Returns -1 in case of error, the number of bytes read if everything went
328  * well.
329  *****************************************************************************/
330 static int dvdplay_Read( input_thread_t * p_input,
331                          byte_t * p_buffer, size_t i_count )
332 {
333     dvd_data_t *    p_dvd;
334     off_t           i_read;
335
336     p_dvd = (dvd_data_t *)p_input->p_access_data;
337
338     vlc_mutex_lock( &p_input->stream.stream_lock );
339     i_read = LB2OFF( dvdplay_read( p_dvd->vmg, p_buffer, OFF2LB( i_count ) ) );
340     vlc_mutex_unlock( &p_input->stream.stream_lock );
341
342     return i_read;
343 }
344
345 /*****************************************************************************
346  * dvdplay_Seek : Goes to a given position on the stream.
347  *****************************************************************************
348  * This one is used by the input and translate chronological position from
349  * input to logical position on the device.
350  * The lock should be taken before calling this function.
351  *****************************************************************************/
352 static void dvdplay_Seek( input_thread_t * p_input, off_t i_off )
353 {
354     dvd_data_t *     p_dvd;
355
356     p_dvd = (dvd_data_t *)p_input->p_access_data;
357
358     vlc_mutex_lock( &p_input->stream.stream_lock );
359
360     dvdplay_seek( p_dvd->vmg, OFF2LB( i_off ) );
361
362     p_input->stream.p_selected_area->i_tell  =
363         LB2OFF( dvdplay_position( p_dvd->vmg ) ) -
364         p_input->stream.p_selected_area->i_start;
365
366     vlc_mutex_unlock( &p_input->stream.stream_lock );
367
368     return;
369 }
370
371
372 /*****************************************************************************
373  * pf_vmg_callback: called by libdvdplay when some event happens
374  *****************************************************************************
375  * The stream lock has to be taken before entering here
376  *****************************************************************************/
377 static void pf_vmg_callback( void* p_args, dvdplay_event_t event )
378 {
379     input_thread_t *    p_input;
380     dvd_data_t *        p_dvd;
381     vlc_value_t         val;
382     unsigned int        i;
383
384     p_input = (input_thread_t*)p_args;
385     p_dvd   = (dvd_data_t*)p_input->p_access_data;
386
387     switch( event )
388     {
389     case NEW_DOMAIN:
390         break;
391     case NEW_VTS:
392         break;
393     case NEW_FILE:
394
395         break;
396     case NEW_PGC:
397         /* prevent intf to try to seek  by default */
398         p_input->stream.b_seekable = 0;
399
400         i = dvdplay_title_cur( p_dvd->vmg );
401         if( i != p_input->stream.p_selected_area->i_id )
402         {
403             /* the title number has changed: update area */
404             msg_Warn( p_input, "new title %d (%d)", i,
405                                p_input->stream.p_selected_area->i_id );
406             dvdNewArea( p_input,
407                         p_input->stream.pp_areas[i] );
408         }
409
410         /* new pgc in same title: reinit ES */
411         dvdNewPGC( p_input );
412
413         p_input->stream.b_changed = 1;
414
415         break;
416     case NEW_PG:
417         /* update current chapter */
418         p_input->stream.p_selected_area->i_part =
419             dvdplay_chapter_cur( p_dvd->vmg );
420         break;
421     case NEW_CELL:
422         p_dvd->b_end_of_cell = 0;
423         break;
424     case END_OF_CELL:
425         p_dvd->b_end_of_cell = 1;
426         break;
427     case JUMP:
428         dvdplay_ES( p_input );
429         break;
430     case STILL_TIME:
431         /* we must pause only from demux
432          * when the data in cache has been decoded */
433         p_dvd->i_still_time = dvdplay_still_time( p_dvd->vmg );
434         msg_Dbg( p_input, "still time %d", p_dvd->i_still_time );
435         break;
436     case COMPLETE_VIDEO:
437         break;
438     case NEW_HIGHLIGHT:
439         if( var_Get( p_input, "highlight-mutex", &val ) == VLC_SUCCESS )
440         {
441             vlc_mutex_t *p_mutex = val.p_address;
442             vlc_mutex_lock( p_mutex );
443
444             /* Retrieve the highlight from dvdplay */
445             dvdplay_highlight( p_dvd->vmg, &p_dvd->hli );
446
447             if( p_dvd->hli.i_x_start || p_dvd->hli.i_y_start ||
448                 p_dvd->hli.i_x_end || p_dvd->hli.i_y_end )
449             {
450                 /* Fill our internal variables with this data */
451                 val.i_int = p_dvd->hli.i_x_start;
452                 var_Set( p_input, "x-start", val );
453                 val.i_int = p_dvd->hli.i_y_start;
454                 var_Set( p_input, "y-start", val );
455                 val.i_int = p_dvd->hli.i_x_end;
456                 var_Set( p_input, "x-end", val );
457                 val.i_int = p_dvd->hli.i_y_end;
458                 var_Set( p_input, "y-end", val );
459
460                 val.p_address = (void *)p_dvd->hli.pi_color;
461                 var_Set( p_input, "color", val );
462                 val.p_address = (void *)p_dvd->hli.pi_contrast;
463                 var_Set( p_input, "contrast", val );
464
465                 /* Tell the SPU decoder that there's a new highlight */
466                 val.b_bool = VLC_TRUE;
467             }
468             else
469             {
470                 /* Turn off the highlight */
471                 val.b_bool = VLC_FALSE;
472             }
473             var_Set( p_input, "highlight", val );
474
475             vlc_mutex_unlock( p_mutex );
476         }
477         break;
478     default:
479         msg_Err( p_input, "unknown event from libdvdplay (%d)", event );
480     }
481
482     return;
483 }
484
485 static int dvdNewArea( input_thread_t * p_input, input_area_t * p_area )
486 {
487     dvd_data_t *    p_dvd;
488     int             i_angle_nb, i_angle;
489     int             i;
490
491     p_dvd = (dvd_data_t*)p_input->p_access_data;
492
493     p_input->stream.p_selected_area = p_area;
494
495     /*
496      * One program for each angle
497      */
498     while( p_input->stream.i_pgrm_number )
499     {
500         input_DelProgram( p_input, p_input->stream.pp_programs[0] );
501     }
502
503     input_AddProgram( p_input, 1, sizeof( stream_ps_data_t ) );
504     p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
505
506     dvdplay_angle_info( p_dvd->vmg, &i_angle_nb, &i_angle );
507     for( i = 1 ; i < i_angle_nb ; i++ )
508     {
509         input_AddProgram( p_input, i+1, 0 );
510     }
511
512     dvdplay_SetProgram( p_input,
513                         p_input->stream.pp_programs[i_angle-1] );
514
515 //    dvdNewPGC( p_input );
516
517     /* No PSM to read in DVD mode, we already have all information */
518     p_input->stream.p_selected_program->b_is_ok = 1;
519
520     return 0;
521 }
522
523 static int dvdNewPGC( input_thread_t * p_input )
524 {
525     dvd_data_t *    p_dvd;
526 //    int             i_audio_nr  = -1;
527 //    int             i_audio     = -1;
528 //    int             i_subp_nr   = -1;
529 //    int             i_subp      = -1;
530 //    int             i_sec;
531
532     p_dvd = (dvd_data_t*)p_input->p_access_data;
533
534 //    dvdplay_audio_info( p_dvd->vmg, &i_audio_nr, &i_audio );
535 //    dvdplay_subp_info( p_dvd->vmg, &i_subp_nr, &i_subp );
536
537     dvdplay_ES( p_input );
538     p_input->stream.p_selected_area->i_start =
539         LB2OFF( dvdplay_title_first( p_dvd->vmg ) );
540     p_input->stream.p_selected_area->i_size  =
541         LB2OFF( dvdplay_title_end ( p_dvd->vmg ) ) -
542         p_input->stream.p_selected_area->i_start;
543     p_input->stream.p_selected_area->i_tell = 0;
544
545     if( p_input->stream.p_selected_area->i_size > 0 )
546     {
547         p_input->stream.b_seekable = 1;
548     }
549     else
550     {
551         p_input->stream.b_seekable = 0;
552     }
553
554 #if 0
555     i_sec = dvdplay_title_time( p_dvd->vmg );
556     msg_Dbg( p_input, "title time: %d:%02d:%02d (%d)",
557                      i_sec/3600, (i_sec%3600)/60, i_sec%60, i_sec );
558 #endif
559
560     return 0;
561 }
562