1
/*****************************************************************************
2
* access.c: access capabilities for dvdplay plugin.
3
*****************************************************************************
4
* Copyright (C) 2001 VideoLAN
5
* $Id: access.c 6961 2004-03-05 17:34:23Z sam $
7
* Author: Stļæ½phane Borel <stef@via.ecp.fr>
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.
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.
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
*****************************************************************************/
24
/*****************************************************************************
26
*****************************************************************************/
31
#include <vlc/input.h>
33
#include "../../demux/mpeg/system.h"
40
#include <sys/types.h>
44
#ifdef STRNCASECMP_IN_STRINGS_H
53
/*****************************************************************************
55
*****************************************************************************/
56
/* called from outside */
57
static int dvdplay_SetArea ( input_thread_t *, input_area_t * );
58
static int dvdplay_SetProgram ( input_thread_t *, pgrm_descriptor_t * );
59
static ssize_t dvdplay_Read ( input_thread_t *, byte_t *, size_t );
60
static void dvdplay_Seek ( input_thread_t *, off_t );
62
static void pf_vmg_callback ( void*, dvdplay_event_t );
64
/* only from inside */
65
static int dvdNewArea( input_thread_t *, input_area_t * );
66
static int dvdNewPGC ( input_thread_t * );
69
static int MenusCallback( vlc_object_t *, char const *,
70
vlc_value_t, vlc_value_t, void * );
72
/*****************************************************************************
73
* OpenDVD: open libdvdplay
74
*****************************************************************************/
75
int E_(OpenDVD) ( vlc_object_t *p_this )
77
input_thread_t * p_input = (input_thread_t *)p_this;
80
input_area_t * p_area;
81
unsigned int i_title_nr;
83
unsigned int i_chapter;
86
vlc_value_t val, text;
88
p_dvd = malloc( sizeof(dvd_data_t) );
91
msg_Err( p_input, "out of memory" );
95
p_input->p_access_data = (void *)p_dvd;
97
p_input->pf_read = dvdplay_Read;
98
p_input->pf_seek = dvdplay_Seek;
99
p_input->pf_set_area = dvdplay_SetArea;
100
p_input->pf_set_program = dvdplay_SetProgram;
103
if( ( psz_source = dvdplay_ParseCL( p_input,
104
&i_title, &i_chapter, &i_angle ) ) == NULL )
110
/* Open libdvdplay */
111
p_dvd->vmg = dvdplay_open( psz_source, pf_vmg_callback, (void*)p_input );
113
if( p_dvd->vmg == NULL )
115
msg_Warn( p_input, "cannot open %s", psz_source );
121
/* free allocated strings */
124
p_dvd->p_intf = NULL;
126
p_dvd->i_still_time = 0;
131
/* Set stream and area data */
132
vlc_mutex_lock( &p_input->stream.stream_lock );
134
/* If we are here we can control the pace... */
135
p_input->stream.b_pace_control = 1;
136
/* seek is only allowed when we have size info */
137
p_input->stream.b_seekable = 0;
139
/* Initialize ES structures */
140
input_InitStream( p_input, sizeof( stream_ps_data_t ) );
142
/* disc input method */
143
p_input->stream.i_method = INPUT_METHOD_DVD;
145
i_title_nr = dvdplay_title_nr( p_dvd->vmg );
146
#define area p_input->stream.pp_areas
148
/* Area 0 for menu */
149
area[0]->i_plugin_data = 0;
150
input_DelArea( p_input, p_input->stream.pp_areas[0] );
151
input_AddArea( p_input, 0, 1 );
153
for( i = 1 ; i <= i_title_nr ; i++ )
155
input_AddArea( p_input, i, dvdplay_chapter_nr( p_dvd->vmg, i ) );
156
area[i]->i_plugin_data = 0;
159
msg_Dbg( p_input, "number of titles: %d", i_title_nr );
161
i_title = i_title <= i_title_nr ? i_title : 0;
163
p_area = p_input->stream.pp_areas[i_title];
164
p_area->i_part = i_chapter;
165
p_input->stream.p_selected_area = NULL;
167
/* set title, chapter, audio and subpic */
168
if( dvdplay_SetArea( p_input, p_area ) )
170
vlc_mutex_unlock( &p_input->stream.stream_lock );
174
if( i_angle <= p_input->stream.i_pgrm_number )
176
dvdplay_SetProgram( p_input,
177
p_input->stream.pp_programs[i_angle - 1] );
180
vlc_mutex_unlock( &p_input->stream.stream_lock );
182
if( !p_input->psz_demux || !*p_input->psz_demux )
184
p_input->psz_demux = "dvdplay";
187
/* FIXME: we might lose variables here */
188
var_Create( p_input, "x-start", VLC_VAR_INTEGER );
189
var_Create( p_input, "y-start", VLC_VAR_INTEGER );
190
var_Create( p_input, "x-end", VLC_VAR_INTEGER );
191
var_Create( p_input, "y-end", VLC_VAR_INTEGER );
193
var_Create( p_input, "color", VLC_VAR_ADDRESS );
194
var_Create( p_input, "contrast", VLC_VAR_ADDRESS );
196
var_Create( p_input, "highlight", VLC_VAR_BOOL );
197
var_Create( p_input, "highlight-mutex", VLC_VAR_MUTEX );
199
/* Create a few object variables used for navigation in the interfaces */
200
var_Create( p_input, "dvd_menus",
201
VLC_VAR_INTEGER | VLC_VAR_HASCHOICE | VLC_VAR_ISCOMMAND );
202
text.psz_string = _("DVD menus");
203
var_Change( p_input, "dvd_menus", VLC_VAR_SETTEXT, &text, NULL );
204
var_AddCallback( p_input, "dvd_menus", MenusCallback, NULL );
205
val.i_int = ROOT_MENU; text.psz_string = _("Root");
206
var_Change( p_input, "dvd_menus", VLC_VAR_ADDCHOICE, &val, &text );
207
val.i_int = TITLE_MENU; text.psz_string = _("Title");
208
var_Change( p_input, "dvd_menus", VLC_VAR_ADDCHOICE, &val, &text );
209
val.i_int = PART_MENU; text.psz_string = _("Chapter");
210
var_Change( p_input, "dvd_menus", VLC_VAR_ADDCHOICE, &val, &text );
211
val.i_int = SUBPICTURE_MENU; text.psz_string = _("Subtitle");
212
var_Change( p_input, "dvd_menus", VLC_VAR_ADDCHOICE, &val, &text );
213
val.i_int = AUDIO_MENU; text.psz_string = _("Audio");
214
var_Change( p_input, "dvd_menus", VLC_VAR_ADDCHOICE, &val, &text );
215
val.i_int = ANGLE_MENU; text.psz_string = _("Angle");
216
var_Change( p_input, "dvd_menus", VLC_VAR_ADDCHOICE, &val, &text );
217
val.i_int = 99; text.psz_string = _("Resume");
218
var_Change( p_input, "dvd_menus", VLC_VAR_ADDCHOICE, &val, &text );
223
/*****************************************************************************
224
* CloseDVD: close libdvdplay
225
*****************************************************************************/
226
void E_(CloseDVD) ( vlc_object_t *p_this )
228
input_thread_t * p_input = (input_thread_t *)p_this;
229
dvd_data_t * p_dvd = (dvd_data_t *)p_input->p_access_data;
231
var_Destroy( p_input, "highlight-mutex" );
232
var_Destroy( p_input, "highlight" );
234
var_Destroy( p_input, "x-start" );
235
var_Destroy( p_input, "x-end" );
236
var_Destroy( p_input, "y-start" );
237
var_Destroy( p_input, "y-end" );
239
var_Destroy( p_input, "color" );
240
var_Destroy( p_input, "contrast" );
242
/* close libdvdplay */
243
dvdplay_close( p_dvd->vmg );
246
p_input->p_access_data = NULL;
250
/*****************************************************************************
251
* dvdplay_SetProgram: set dvd angle.
252
*****************************************************************************
253
* This is actually a hack to make angle change through vlc interface with
254
* no need for a specific button.
255
*****************************************************************************/
256
static int dvdplay_SetProgram( input_thread_t * p_input,
257
pgrm_descriptor_t * p_program )
259
if( p_input->stream.p_selected_program != p_program )
265
p_dvd = (dvd_data_t*)(p_input->p_access_data);
266
i_angle = p_program->i_number;
268
if( !dvdplay_angle( p_dvd->vmg, i_angle ) )
270
memcpy( p_program, p_input->stream.p_selected_program,
271
sizeof(pgrm_descriptor_t) );
272
p_program->i_number = i_angle;
273
p_input->stream.p_selected_program = p_program;
275
msg_Dbg( p_input, "angle %d selected", i_angle );
278
/* Update the navigation variables without triggering a callback */
279
val.i_int = p_program->i_number;
280
var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
286
/*****************************************************************************
287
* dvdplay_SetArea: initialize input data for title x, chapter y.
288
* It should be called for each user navigation request.
289
*****************************************************************************
290
* Take care that i_title starts from 0 (vmg) and i_chapter start from 1.
291
* Note that you have to take the lock before entering here.
292
*****************************************************************************/
293
static int dvdplay_SetArea( input_thread_t * p_input, input_area_t * p_area )
298
p_dvd = (dvd_data_t*)p_input->p_access_data;
303
if( p_area != p_input->stream.p_selected_area )
307
/* prevent intf to try to seek */
308
p_input->stream.b_seekable = 0;
310
/* Store selected chapter */
311
i_chapter = p_area->i_part;
313
dvdNewArea( p_input, p_area );
315
dvdNewPGC( p_input );
317
dvdplay_start( p_dvd->vmg, p_area->i_id );
319
p_area->i_part = i_chapter;
324
p_area = p_input->stream.p_selected_area;
331
if( (int)p_area->i_part != dvdplay_chapter_cur( p_dvd->vmg ) )
333
if( ( p_area->i_part > 0 ) &&
334
( p_area->i_part <= p_area->i_part_nb ))
336
dvdplay_pg( p_dvd->vmg, p_area->i_part );
338
p_area->i_part = dvdplay_chapter_cur( p_dvd->vmg );
341
/* warn interface that something has changed */
343
LB2OFF( dvdplay_position( p_dvd->vmg ) ) - p_area->i_start;
344
p_input->stream.b_changed = 1;
346
/* Update the navigation variables without triggering a callback */
347
val.i_int = p_area->i_part;
348
var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val, NULL );
353
/*****************************************************************************
354
* dvdplay_Read: reads data packets.
355
*****************************************************************************
356
* Returns -1 in case of error, the number of bytes read if everything went
358
*****************************************************************************/
359
static ssize_t dvdplay_Read( input_thread_t * p_input,
360
byte_t * p_buffer, size_t i_count )
365
p_dvd = (dvd_data_t *)p_input->p_access_data;
367
vlc_mutex_lock( &p_input->stream.stream_lock );
368
i_read = LB2OFF( dvdplay_read( p_dvd->vmg, p_buffer, OFF2LB( i_count ) ) );
369
vlc_mutex_unlock( &p_input->stream.stream_lock );
374
/*****************************************************************************
375
* dvdplay_Seek : Goes to a given position on the stream.
376
*****************************************************************************
377
* This one is used by the input and translate chronological position from
378
* input to logical position on the device.
379
* The lock should be taken before calling this function.
380
*****************************************************************************/
381
static void dvdplay_Seek( input_thread_t * p_input, off_t i_off )
385
p_dvd = (dvd_data_t *)p_input->p_access_data;
387
vlc_mutex_lock( &p_input->stream.stream_lock );
389
dvdplay_seek( p_dvd->vmg, OFF2LB( i_off ) );
391
p_input->stream.p_selected_area->i_tell =
392
LB2OFF( dvdplay_position( p_dvd->vmg ) ) -
393
p_input->stream.p_selected_area->i_start;
395
vlc_mutex_unlock( &p_input->stream.stream_lock );
401
/*****************************************************************************
402
* pf_vmg_callback: called by libdvdplay when some event happens
403
*****************************************************************************
404
* The stream lock has to be taken before entering here
405
*****************************************************************************/
406
static void pf_vmg_callback( void* p_args, dvdplay_event_t event )
408
input_thread_t * p_input;
413
p_input = (input_thread_t*)p_args;
414
p_dvd = (dvd_data_t*)p_input->p_access_data;
425
/* prevent intf to try to seek by default */
426
p_input->stream.b_seekable = 0;
428
i = dvdplay_title_cur( p_dvd->vmg );
429
if( i != p_input->stream.p_selected_area->i_id )
431
/* the title number has changed: update area */
432
msg_Warn( p_input, "new title %d (%d)", i,
433
p_input->stream.p_selected_area->i_id );
435
p_input->stream.pp_areas[i] );
438
/* new pgc in same title: reinit ES */
439
dvdplay_ES( p_input );
440
dvdNewPGC( p_input );
442
p_input->stream.b_changed = 1;
446
dvdplay_ES( p_input );
447
dvdNewPGC( p_input );
449
/* update current chapter */
450
p_input->stream.p_selected_area->i_part =
451
dvdplay_chapter_cur( p_dvd->vmg );
453
p_input->stream.p_selected_area->i_tell =
454
LB2OFF( dvdplay_position( p_dvd->vmg ) ) -
455
p_input->stream.p_selected_area->i_start;
457
/* warn interface that something has changed */
458
p_input->stream.b_changed = 1;
460
/* Update the navigation variables without triggering a callback */
461
val.i_int = p_input->stream.p_selected_area->i_part;
462
var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val, NULL );
465
p_dvd->b_end_of_cell = 0;
468
p_dvd->b_end_of_cell = 1;
471
/* we must pause only from demux
472
* when the data in cache has been decoded */
473
p_dvd->i_still_time = dvdplay_still_time( p_dvd->vmg );
474
msg_Dbg( p_input, "still time %d", p_dvd->i_still_time );
479
if( var_Get( p_input, "highlight-mutex", &val ) == VLC_SUCCESS )
481
vlc_mutex_t *p_mutex = val.p_address;
482
vlc_mutex_lock( p_mutex );
484
/* Retrieve the highlight from dvdplay */
485
dvdplay_highlight( p_dvd->vmg, &p_dvd->hli );
487
if( p_dvd->hli.i_x_start || p_dvd->hli.i_y_start ||
488
p_dvd->hli.i_x_end || p_dvd->hli.i_y_end )
490
/* Fill our internal variables with this data */
491
val.i_int = p_dvd->hli.i_x_start;
492
var_Set( p_input, "x-start", val );
493
val.i_int = p_dvd->hli.i_y_start;
494
var_Set( p_input, "y-start", val );
495
val.i_int = p_dvd->hli.i_x_end;
496
var_Set( p_input, "x-end", val );
497
val.i_int = p_dvd->hli.i_y_end;
498
var_Set( p_input, "y-end", val );
500
val.p_address = (void *)p_dvd->hli.pi_color;
501
var_Set( p_input, "color", val );
502
val.p_address = (void *)p_dvd->hli.pi_contrast;
503
var_Set( p_input, "contrast", val );
505
/* Tell the SPU decoder that there's a new highlight */
506
val.b_bool = VLC_TRUE;
510
/* Turn off the highlight */
511
val.b_bool = VLC_FALSE;
513
var_Set( p_input, "highlight", val );
515
vlc_mutex_unlock( p_mutex );
519
msg_Err( p_input, "unknown event from libdvdplay (%d)", event );
525
static int dvdNewArea( input_thread_t * p_input, input_area_t * p_area )
528
int i_angle_nb, i_angle;
532
p_dvd = (dvd_data_t*)p_input->p_access_data;
534
p_input->stream.p_selected_area = p_area;
537
* One program for each angle
539
while( p_input->stream.i_pgrm_number )
541
input_DelProgram( p_input, p_input->stream.pp_programs[0] );
544
input_AddProgram( p_input, 1, sizeof( stream_ps_data_t ) );
545
p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
547
dvdplay_angle_info( p_dvd->vmg, &i_angle_nb, &i_angle );
548
for( i = 1 ; i < i_angle_nb ; i++ )
550
input_AddProgram( p_input, i+1, 0 );
554
dvdplay_SetProgram( p_input,
555
p_input->stream.pp_programs[i_angle-1] );
557
dvdplay_SetProgram( p_input,
558
p_input->stream.pp_programs[0] );
560
/* No PSM to read in DVD mode, we already have all information */
561
p_input->stream.p_selected_program->b_is_ok = 1;
564
dvdplay_ES( p_input );
566
/* Update the navigation variables without triggering a callback */
567
val.i_int = p_area->i_id;
568
var_Change( p_input, "title", VLC_VAR_SETVALUE, &val, NULL );
569
var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
570
for( i = 1; (unsigned int)i <= p_area->i_part_nb; i++ )
573
var_Change( p_input, "chapter", VLC_VAR_ADDCHOICE, &val, NULL );
576
/* Update the navigation variables without triggering a callback */
577
val.i_int = p_area->i_part;
578
var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val, NULL );
583
static int dvdNewPGC( input_thread_t * p_input )
585
dvd_data_t * p_dvd = (dvd_data_t*)p_input->p_access_data;
587
p_input->stream.p_selected_area->i_start =
588
LB2OFF( dvdplay_title_first( p_dvd->vmg ) );
589
p_input->stream.p_selected_area->i_size =
590
LB2OFF( dvdplay_title_end ( p_dvd->vmg ) ) -
591
p_input->stream.p_selected_area->i_start;
592
p_input->stream.p_selected_area->i_tell = 0;
594
if( p_input->stream.p_selected_area->i_size > 0 )
596
p_input->stream.b_seekable = 1;
600
p_input->stream.b_seekable = 0;
606
static int MenusCallback( vlc_object_t *p_this, char const *psz_name,
607
vlc_value_t oldval, vlc_value_t newval, void *p_arg )
609
input_thread_t * p_input;
612
p_input = (input_thread_t*)p_this;
613
p_dvd = (dvd_data_t*)p_input->p_access_data;
615
vlc_mutex_lock( &p_input->stream.stream_lock );
616
if( newval.i_int < 99 )
617
dvdplay_menu( p_dvd->vmg, newval.i_int, 0 );
619
dvdplay_resume( p_dvd->vmg );
620
vlc_mutex_unlock( &p_input->stream.stream_lock );
622
if( p_dvd->p_intf ) dvdIntfResetStillTime( p_dvd->p_intf );