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