dvdnav.c 45.9 KB
Newer Older
1
/*****************************************************************************
gbazin's avatar
   
gbazin committed
2
 * dvdnav.c: DVD module using the dvdnav library.
3
 *****************************************************************************
4
 * Copyright (C) 2004-2009 the VideoLAN team
5
 * $Id$
6
7
8
9
10
11
12
13
14
15
16
17
18
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
JP Dinger's avatar
JP Dinger committed
19
20
21
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
23
24
25
26
27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

28
29
30
31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32
#include <assert.h>
33
#include <vlc_common.h>
34
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
35
36
37
#include <vlc_input.h>
#include <vlc_access.h>
#include <vlc_demux.h>
38
#include <vlc_charset.h>
39
#include <vlc_fs.h>
Ilkka Ollakka's avatar
Ilkka Ollakka committed
40
#include <vlc_url.h>
41
#include <vlc_vout.h>
zorglub's avatar
zorglub committed
42

43
#include <vlc_dialog.h>
44

45
46
47
#ifdef HAVE_UNISTD_H
#   include <unistd.h>
#endif
48
#include <sys/types.h>
49
50
51
52
53
54
#ifdef HAVE_SYS_STAT_H
#   include <sys/stat.h>
#endif
#ifdef HAVE_FCNTL_H
#   include <fcntl.h>
#endif
Felix Paul Kühne's avatar
Felix Paul Kühne committed
55
#include <errno.h>
56

57
58
#include <vlc_keys.h>
#include <vlc_iso_lang.h>
59

60
/* FIXME we should find a better way than including that */
61
#include "../../src/text/iso-639_def.h"
62
63


64
65
#include <dvdnav/dvdnav.h>

66
#include "../demux/ps.h"
67
68
69
70

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
71
72
#define ANGLE_TEXT N_("DVD angle")
#define ANGLE_LONGTEXT N_( \
zorglub's avatar
zorglub committed
73
     "Default DVD angle." )
74

75
#define CACHING_TEXT N_("Caching value in ms")
76
#define CACHING_LONGTEXT N_( \
77
    "Caching value for DVDs. This "\
zorglub's avatar
zorglub committed
78
    "value should be set in milliseconds." )
79
80
#define MENU_TEXT N_("Start directly in menu")
#define MENU_LONGTEXT N_( \
zorglub's avatar
zorglub committed
81
82
    "Start the DVD directly in the main menu. This "\
    "will try to skip all the useless warning introductions." )
83

84
85
#define LANGUAGE_DEFAULT ("en")

Laurent Aimar's avatar
Laurent Aimar committed
86
87
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );
88

89
90
91
92
93
vlc_module_begin ()
    set_shortname( N_("DVD with menus") )
    set_description( N_("DVDnav Input") )
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_ACCESS )
94
    add_integer( "dvdnav-angle", 1, ANGLE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
95
        ANGLE_LONGTEXT, false )
96
    add_integer( "dvdnav-caching", DEFAULT_PTS_DELAY / 1000,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
97
        CACHING_TEXT, CACHING_LONGTEXT, true )
98
    add_bool( "dvdnav-menu", true,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
99
        MENU_TEXT, MENU_LONGTEXT, false )
100
    set_capability( "access_demux", 5 )
101
    add_shortcut( "dvd", "dvdnav", "file" )
102
103
    set_callbacks( Open, Close )
vlc_module_end ()
104

105
106
107
/* Shall we use libdvdnav's read ahead cache? */
#define DVD_READ_CACHE 1

108
109
110
111
112
113
114
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
struct demux_sys_t
{
    dvdnav_t    *dvdnav;

Laurent Aimar's avatar
Laurent Aimar committed
115
116
117
    /* */
    bool        b_reset_pcr;

118
119
120
121
122
    struct
    {
        bool         b_created;
        bool         b_enabled;
        vlc_mutex_t  lock;
123
        vlc_timer_t  timer;
124
125
    } still;

126
127
    /* track */
    ps_track_t  tk[PS_TK_COUNT];
128
    int         i_mux_rate;
129
130
131
132
133

    /* for spu variables */
    input_thread_t *p_input;

    /* event */
134
    vout_thread_t *p_vout;
135

136
137
138
    /* palette for menus */
    uint32_t clut[16];
    uint8_t  palette[4][4];
139
    bool b_spu_change;
140

141
142
143
144
145
146
    /* Aspect ration */
    struct {
        unsigned i_num;
        unsigned i_den;
    } sar;

147
    /* */
Laurent Aimar's avatar
Laurent Aimar committed
148
149
    int           i_title;
    input_title_t **title;
150
151
152

    /* lenght of program group chain */
    mtime_t     i_pgc_length;
153
154
    int         i_vobu_index;
    int         i_vobu_flush;
155
156
};

Laurent Aimar's avatar
Laurent Aimar committed
157
static int Control( demux_t *, int, va_list );
158
static int Demux( demux_t * );
159
static int DemuxBlock( demux_t *, const uint8_t *, int );
160
static void DemuxForceStill( demux_t * );
161

162
static void DemuxTitles( demux_t * );
Laurent Aimar's avatar
Laurent Aimar committed
163
static void ESSubtitleUpdate( demux_t * );
164
static void ButtonUpdate( demux_t *, bool );
Laurent Aimar's avatar
Laurent Aimar committed
165

166
static void ESNew( demux_t *, int );
167
static int ProbeDVD( const char * );
168

Laurent Aimar's avatar
Laurent Aimar committed
169
static char *DemuxGetLanguageCode( demux_t *p_demux, const char *psz_var );
170

171
172
static int ControlInternal( demux_t *, int, ... );

173
static void StillTimer( void * );
174

175
176
static int EventKey( vlc_object_t *, char const *,
                     vlc_value_t, vlc_value_t, void * );
177
178
179
180
static int EventMouse( vlc_object_t *, char const *,
                       vlc_value_t, vlc_value_t, void * );
static int EventIntf( vlc_object_t *, char const *,
                      vlc_value_t, vlc_value_t, void * );
181

182
183
184
/*****************************************************************************
 * DemuxOpen:
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
185
static int Open( vlc_object_t *p_this )
186
{
gbazin's avatar
   
gbazin committed
187
188
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys;
189
    dvdnav_t    *p_dvdnav;
190
    int         i_angle;
191
    char        *psz_file;
192
    char        *psz_code;
193
194
195
196
197
    bool forced = false;

    if( p_demux->psz_access != NULL
     && !strncmp(p_demux->psz_access, "dvd", 3) )
        forced = true;
198

199
    if( !p_demux->psz_file || !*p_demux->psz_file )
200
    {
201
        /* Only when selected */
202
        if( !forced )
203
            return VLC_EGENERIC;
204

205
        psz_file = var_InheritString( p_this, "dvd" );
206
    }
207
    else
208
        psz_file = strdup( p_demux->psz_file );
209

210
#if defined( WIN32 ) || defined( __OS2__ )
211
    if( psz_file != NULL )
212
    {
213
214
215
216
        /* Remove trailing backslash, otherwise dvdnav_open will fail */
        size_t flen = strlen( psz_file );
        if( flen > 0 && psz_file[flen - 1] == '\\' )
            psz_file[flen - 1] = '\0';
217
    }
218
219
    else
        psz_file = strdup("");
220
#endif
221
222
    if( unlikely(psz_file == NULL) )
        return VLC_EGENERIC;
223

224
    /* Try some simple probing to avoid going through dvdnav_open too often */
225
    if( !forced && ProbeDVD( psz_file ) != VLC_SUCCESS )
226
    {
227
        free( psz_file );
228
229
230
        return VLC_EGENERIC;
    }

231
    /* Open dvdnav */
232
233
234
235
236
    const char *psz_path = ToLocale( psz_file );
    if( dvdnav_open( &p_dvdnav, psz_path ) != DVDNAV_STATUS_OK )
        p_dvdnav = NULL;
    LocaleFree( psz_path );
    if( p_dvdnav == NULL )
gbazin's avatar
   
gbazin committed
237
    {
238
239
        msg_Warn( p_demux, "cannot open DVD (%s)", psz_file);
        free( psz_file );
gbazin's avatar
   
gbazin committed
240
241
        return VLC_EGENERIC;
    }
242
    free( psz_file );
gbazin's avatar
   
gbazin committed
243

244
    /* Fill p_demux field */
245
    DEMUX_INIT_COMMON(); p_sys = p_demux->p_sys;
246
    p_sys->dvdnav = p_dvdnav;
Laurent Aimar's avatar
Laurent Aimar committed
247
    p_sys->b_reset_pcr = false;
248
249

    ps_track_init( p_sys->tk );
250
251
    p_sys->sar.i_num = 0;
    p_sys->sar.i_den = 0;
252
    p_sys->i_mux_rate = 0;
253
    p_sys->i_pgc_length = 0;
254
    p_sys->b_spu_change = false;
255
256
    p_sys->i_vobu_index = 0;
    p_sys->i_vobu_flush = 0;
257

258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
    if( 1 )
    {
        // Hack for libdvdnav CVS.
        // Without it dvdnav_get_number_of_titles() fails.
        // Remove when fixed in libdvdnav CVS.
        uint8_t buffer[DVD_VIDEO_LB_LEN];
        int i_event, i_len;

        if( dvdnav_get_next_block( p_sys->dvdnav, buffer, &i_event, &i_len )
              == DVDNAV_STATUS_ERR )
        {
            msg_Warn( p_demux, "dvdnav_get_next_block failed" );
        }

        dvdnav_sector_search( p_sys->dvdnav, 0, SEEK_SET );
    }

275
    /* Configure dvdnav */
276
277
    if( dvdnav_set_readahead_flag( p_sys->dvdnav, DVD_READ_CACHE ) !=
          DVDNAV_STATUS_OK )
278
    {
hartman's avatar
hartman committed
279
        msg_Warn( p_demux, "cannot set read-a-head flag" );
280
    }
gbazin's avatar
   
gbazin committed
281
282
283

    if( dvdnav_set_PGC_positioning_flag( p_sys->dvdnav, 1 ) !=
          DVDNAV_STATUS_OK )
284
285
286
    {
        msg_Warn( p_demux, "cannot set PGC positioning flag" );
    }
gbazin's avatar
   
gbazin committed
287

288
289
290
291
    /* Set menu language
     * XXX A menu-language may be better than sub-language */
    psz_code = DemuxGetLanguageCode( p_demux, "sub-language" );
    if( dvdnav_menu_language_select( p_sys->dvdnav, psz_code ) !=
292
        DVDNAV_STATUS_OK )
293
    {
294
        msg_Warn( p_demux, "can't set menu language to '%s' (%s)",
295
296
297
298
                  psz_code, dvdnav_err_to_string( p_sys->dvdnav ) );
        /* We try to fall back to 'en' */
        if( strcmp( psz_code, LANGUAGE_DEFAULT ) )
            dvdnav_menu_language_select( p_sys->dvdnav, (char*)LANGUAGE_DEFAULT );
299
    }
300
    free( psz_code );
301
302
303

    /* Set audio language */
    psz_code = DemuxGetLanguageCode( p_demux, "audio-language" );
hartman's avatar
hartman committed
304
    if( dvdnav_audio_language_select( p_sys->dvdnav, psz_code ) !=
305
        DVDNAV_STATUS_OK )
306
    {
307
308
        msg_Warn( p_demux, "can't set audio language to '%s' (%s)",
                  psz_code, dvdnav_err_to_string( p_sys->dvdnav ) );
309
310
        /* We try to fall back to 'en' */
        if( strcmp( psz_code, LANGUAGE_DEFAULT ) )
311
            dvdnav_audio_language_select( p_sys->dvdnav, (char*)LANGUAGE_DEFAULT );
312
313
314
315
    }
    free( psz_code );

    /* Set spu language */
hartman's avatar
hartman committed
316
317
    psz_code = DemuxGetLanguageCode( p_demux, "sub-language" );
    if( dvdnav_spu_language_select( p_sys->dvdnav, psz_code ) !=
318
        DVDNAV_STATUS_OK )
319
    {
320
321
        msg_Warn( p_demux, "can't set spu language to '%s' (%s)",
                  psz_code, dvdnav_err_to_string( p_sys->dvdnav ) );
322
323
        /* We try to fall back to 'en' */
        if( strcmp( psz_code, LANGUAGE_DEFAULT ) )
324
            dvdnav_spu_language_select(p_sys->dvdnav, (char*)LANGUAGE_DEFAULT );
325
    }
326
    free( psz_code );
327

Laurent Aimar's avatar
Laurent Aimar committed
328
329
    DemuxTitles( p_demux );

ivoire's avatar
ivoire committed
330
    if( var_CreateGetBool( p_demux, "dvdnav-menu" ) )
331
332
333
334
335
    {
        msg_Dbg( p_demux, "trying to go to dvd menu" );

        if( dvdnav_title_play( p_sys->dvdnav, 1 ) != DVDNAV_STATUS_OK )
        {
336
            msg_Err( p_demux, "cannot set title (can't decrypt DVD?)" );
337
            dialog_Fatal( p_demux, _("Playback failure"), "%s",
338
                            _("VLC cannot set the DVD's title. It possibly "
339
                              "cannot decrypt the entire disc.") );
340
341
342
            dvdnav_close( p_sys->dvdnav );
            free( p_sys );
            return VLC_EGENERIC;
343
344
345
346
347
        }

        if( dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Title ) !=
            DVDNAV_STATUS_OK )
        {
348
349
350
351
            /* Try going to menu root */
            if( dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Root ) !=
                DVDNAV_STATUS_OK )
                    msg_Warn( p_demux, "cannot go to dvd menu" );
352
353
354
        }
    }

ivoire's avatar
ivoire committed
355
356
    i_angle = var_CreateGetInteger( p_demux, "dvdnav-angle" );
    if( i_angle <= 0 ) i_angle = 1;
357

Laurent Aimar's avatar
Laurent Aimar committed
358
359
    /* Update default_pts to a suitable value for dvdnav access */
    var_Create( p_demux, "dvdnav-caching", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
360

361
    /* FIXME hack hack hack hack FIXME */
362
    /* Get p_input and create variable */
363
    p_sys->p_input = demux_GetParentInput( p_demux );
364
365
366
367
368
    var_Create( p_sys->p_input, "x-start", VLC_VAR_INTEGER );
    var_Create( p_sys->p_input, "y-start", VLC_VAR_INTEGER );
    var_Create( p_sys->p_input, "x-end", VLC_VAR_INTEGER );
    var_Create( p_sys->p_input, "y-end", VLC_VAR_INTEGER );
    var_Create( p_sys->p_input, "color", VLC_VAR_ADDRESS );
369
    var_Create( p_sys->p_input, "menu-palette", VLC_VAR_ADDRESS );
370
371
372
    var_Create( p_sys->p_input, "highlight", VLC_VAR_BOOL );
    var_Create( p_sys->p_input, "highlight-mutex", VLC_VAR_MUTEX );

373
    /* catch all key event */
374
    var_AddCallback( p_demux->p_libvlc, "key-action", EventKey, p_demux );
375
    /* catch vout creation event */
376
    var_AddCallback( p_sys->p_input, "intf-event", EventIntf, p_demux );
377

378
379
    p_sys->still.b_enabled = false;
    vlc_mutex_init( &p_sys->still.lock );
380
381
382
    if( !vlc_timer_create( &p_sys->still.timer, StillTimer, p_sys ) )
        p_sys->still.b_created = true;

383
384
385
386
    return VLC_SUCCESS;
}

/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
387
 * Close:
388
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
389
static void Close( vlc_object_t *p_this )
390
391
392
393
{
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys = p_demux->p_sys;

394
    /* Stop vout event handler */
395
    var_DelCallback( p_sys->p_input, "intf-event", EventIntf, p_demux );
396
397
398
    if( p_sys->p_vout != NULL )
    {   /* Should not happen, but better be safe than sorry. */
        msg_Warn( p_sys->p_vout, "removing dangling mouse DVD callbacks" );
399
400
        var_DelCallback( p_sys->p_vout, "mouse-moved", EventMouse, p_demux );
        var_DelCallback( p_sys->p_vout, "mouse-clicked", EventMouse, p_demux );
401
402
    }

403
    /* Stop key event handler (FIXME: should really be per-vout too) */
404
    var_DelCallback( p_demux->p_libvlc, "key-action", EventKey, p_demux );
405
406
407

    /* Stop still image handler */
    if( p_sys->still.b_created )
408
        vlc_timer_destroy( p_sys->still.timer );
409
    vlc_mutex_destroy( &p_sys->still.lock );
410
411
412
413
414
415
416
417

    var_Destroy( p_sys->p_input, "highlight-mutex" );
    var_Destroy( p_sys->p_input, "highlight" );
    var_Destroy( p_sys->p_input, "x-start" );
    var_Destroy( p_sys->p_input, "x-end" );
    var_Destroy( p_sys->p_input, "y-start" );
    var_Destroy( p_sys->p_input, "y-end" );
    var_Destroy( p_sys->p_input, "color" );
418
    var_Destroy( p_sys->p_input, "menu-palette" );
419
420

    vlc_object_release( p_sys->p_input );
421

422
    for( int i = 0; i < PS_TK_COUNT; i++ )
423
424
425
426
427
428
429
430
431
    {
        ps_track_t *tk = &p_sys->tk[i];
        if( tk->b_seen )
        {
            es_format_Clean( &tk->fmt );
            if( tk->es ) es_out_Del( p_demux->out, tk->es );
        }
    }

ivoire's avatar
ivoire committed
432
433
434
435
436
    /* Free the array of titles */
    for( int i = 0; i < p_sys->i_title; i++ )
        vlc_input_title_Delete( p_sys->title[i] );
    TAB_CLEAN( p_sys->i_title, p_sys->title );

437
438
439
440
441
    dvdnav_close( p_sys->dvdnav );
    free( p_sys );
}

/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
442
 * Control:
443
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
444
static int Control( demux_t *p_demux, int i_query, va_list args )
445
446
{
    demux_sys_t *p_sys = p_demux->p_sys;
Laurent Aimar's avatar
Laurent Aimar committed
447
448
    input_title_t ***ppp_title;
    int i;
449
450
451

    switch( i_query )
    {
452
        case DEMUX_SET_POSITION:
453
        case DEMUX_GET_POSITION:
454
455
        case DEMUX_GET_TIME:
        case DEMUX_GET_LENGTH:
456
457
        {
            uint32_t pos, len;
458
459
            if( dvdnav_get_position( p_sys->dvdnav, &pos, &len ) !=
                  DVDNAV_STATUS_OK || len == 0 )
460
            {
461
                return VLC_EGENERIC;
462
            }
463

JP Dinger's avatar
JP Dinger committed
464
            switch( i_query )
465
            {
JP Dinger's avatar
JP Dinger committed
466
467
            case DEMUX_GET_POSITION:
                *va_arg( args, double* ) = (double)pos / (double)len;
468
                return VLC_SUCCESS;
JP Dinger's avatar
JP Dinger committed
469
470
471

            case DEMUX_SET_POSITION:
                pos = va_arg( args, double ) * len;
gbazin's avatar
   
gbazin committed
472
473
                if( dvdnav_sector_search( p_sys->dvdnav, pos, SEEK_SET ) ==
                      DVDNAV_STATUS_OK )
474
475
476
                {
                    return VLC_SUCCESS;
                }
JP Dinger's avatar
JP Dinger committed
477
478
479
                break;

            case DEMUX_GET_TIME:
480
                if( p_sys->i_pgc_length > 0 )
481
                {
JP Dinger's avatar
JP Dinger committed
482
                    *va_arg( args, int64_t * ) = p_sys->i_pgc_length*pos/len;
483
484
                    return VLC_SUCCESS;
                }
JP Dinger's avatar
JP Dinger committed
485
486
487
                break;

            case DEMUX_GET_LENGTH:
488
                if( p_sys->i_pgc_length > 0 )
489
                {
JP Dinger's avatar
JP Dinger committed
490
                    *va_arg( args, int64_t * ) = (int64_t)p_sys->i_pgc_length;
491
492
                    return VLC_SUCCESS;
                }
JP Dinger's avatar
JP Dinger committed
493
                break;
494
            }
495
496
497
            return VLC_EGENERIC;
        }

Laurent Aimar's avatar
Laurent Aimar committed
498
499
        /* Special for access_demux */
        case DEMUX_CAN_PAUSE:
500
        case DEMUX_CAN_SEEK:
Laurent Aimar's avatar
Laurent Aimar committed
501
502
        case DEMUX_CAN_CONTROL_PACE:
            /* TODO */
JP Dinger's avatar
JP Dinger committed
503
            *va_arg( args, bool * ) = true;
Laurent Aimar's avatar
Laurent Aimar committed
504
505
506
507
508
509
            return VLC_SUCCESS;

        case DEMUX_SET_PAUSE_STATE:
            return VLC_SUCCESS;

        case DEMUX_GET_TITLE_INFO:
JP Dinger's avatar
JP Dinger committed
510
511
512
513
            ppp_title = va_arg( args, input_title_t*** );
            *va_arg( args, int* ) = p_sys->i_title;
            *va_arg( args, int* ) = 0; /* Title offset */
            *va_arg( args, int* ) = 1; /* Chapter offset */
Laurent Aimar's avatar
Laurent Aimar committed
514
515
516
517
518
519
520
521
522
523
524

            /* Duplicate title infos */
            *ppp_title = malloc( sizeof( input_title_t ** ) * p_sys->i_title );
            for( i = 0; i < p_sys->i_title; i++ )
            {
                (*ppp_title)[i] = vlc_input_title_Duplicate( p_sys->title[i] );
            }
            return VLC_SUCCESS;

        case DEMUX_SET_TITLE:
            i = (int)va_arg( args, int );
525
526
527
528
            if( ( i == 0 && dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Root )
                  != DVDNAV_STATUS_OK ) ||
                ( i != 0 && dvdnav_title_play( p_sys->dvdnav, i )
                  != DVDNAV_STATUS_OK ) )
Laurent Aimar's avatar
Laurent Aimar committed
529
530
531
532
            {
                msg_Warn( p_demux, "cannot set title/chapter" );
                return VLC_EGENERIC;
            }
533
534
            p_demux->info.i_update |=
                INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
Laurent Aimar's avatar
Laurent Aimar committed
535
536
537
538
539
            p_demux->info.i_title = i;
            p_demux->info.i_seekpoint = 0;
            return VLC_SUCCESS;

        case DEMUX_SET_SEEKPOINT:
JP Dinger's avatar
JP Dinger committed
540
            i = va_arg( args, int );
Laurent Aimar's avatar
Laurent Aimar committed
541
542
            if( p_demux->info.i_title == 0 )
            {
JP Dinger's avatar
JP Dinger committed
543
544
545
546
547
548
549
550
551
552
553
554
                static const int argtab[] = {
                    DVD_MENU_Escape,
                    DVD_MENU_Root,
                    DVD_MENU_Title,
                    DVD_MENU_Part,
                    DVD_MENU_Subpicture,
                    DVD_MENU_Audio,
                    DVD_MENU_Angle
                };
                enum { numargs = sizeof(argtab)/sizeof(int) };
                if( (unsigned)i >= numargs || DVDNAV_STATUS_OK !=
                           dvdnav_menu_call(p_sys->dvdnav,argtab[i]) )
Laurent Aimar's avatar
Laurent Aimar committed
555
556
                    return VLC_EGENERIC;
            }
557
            else if( dvdnav_part_play( p_sys->dvdnav, p_demux->info.i_title,
558
                                       i + 1 ) != DVDNAV_STATUS_OK )
Laurent Aimar's avatar
Laurent Aimar committed
559
560
561
562
563
564
565
566
567
            {
                msg_Warn( p_demux, "cannot set title/chapter" );
                return VLC_EGENERIC;
            }
            p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
            p_demux->info.i_seekpoint = i;
            return VLC_SUCCESS;

        case DEMUX_GET_PTS_DELAY:
JP Dinger's avatar
JP Dinger committed
568
569
            *va_arg( args, int64_t * )
                 = (int64_t)var_GetInteger( p_demux, "dvdnav-caching" ) *1000;
Laurent Aimar's avatar
Laurent Aimar committed
570
571
            return VLC_SUCCESS;

572
573
574
575
576
577
578
        case DEMUX_GET_META:
        {
            const char *title_name = NULL;

            dvdnav_get_title_string(p_sys->dvdnav, &title_name);
            if( (NULL != title_name) && ('\0' != title_name[0]) )
            {
579
                vlc_meta_t *p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t* );
580
                vlc_meta_Set( p_meta, vlc_meta_Title, title_name );
581
582
583
584
585
                return VLC_SUCCESS;
            }
            return VLC_EGENERIC;
        }

586
587
588
589
590
591
        /* TODO implement others */
        default:
            return VLC_EGENERIC;
    }
}

592
593
594
595
596
597
598
599
600
601
602
static int ControlInternal( demux_t *p_demux, int i_query, ... )
{
    va_list args;
    int     i_result;

    va_start( args, i_query );
    i_result = Control( p_demux, i_query, args );
    va_end( args );

    return i_result;
}
603
/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
604
 * Demux:
605
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
606
static int Demux( demux_t *p_demux )
607
608
609
{
    demux_sys_t *p_sys = p_demux->p_sys;

610
611
    uint8_t buffer[DVD_VIDEO_LB_LEN];
    uint8_t *packet = buffer;
612
613
614
    int i_event;
    int i_len;

615
#if DVD_READ_CACHE
616
617
    if( dvdnav_get_next_cache_block( p_sys->dvdnav, &packet, &i_event, &i_len )
        == DVDNAV_STATUS_ERR )
618
#else
619
620
    if( dvdnav_get_next_block( p_sys->dvdnav, packet, &i_event, &i_len )
        == DVDNAV_STATUS_ERR )
621
#endif
622
623
624
    {
        msg_Warn( p_demux, "cannot get next block (%s)",
                  dvdnav_err_to_string( p_sys->dvdnav ) );
625
626
627
628
629
        if( p_demux->info.i_title == 0 )
        {
            msg_Dbg( p_demux, "jumping to first title" );
            return ControlInternal( p_demux, DEMUX_SET_TITLE, 1 ) == VLC_SUCCESS ? 1 : -1;
        }
630
631
        return -1;
    }
gbazin's avatar
   
gbazin committed
632

633
634
    switch( i_event )
    {
gbazin's avatar
   
gbazin committed
635
    case DVDNAV_BLOCK_OK:   /* mpeg block */
636
        vlc_mutex_lock( &p_sys->still.lock );
637
        vlc_timer_schedule( p_sys->still.timer, false, 0, 0 );
638
639
        p_sys->still.b_enabled = false;
        vlc_mutex_unlock( &p_sys->still.lock );
Laurent Aimar's avatar
Laurent Aimar committed
640
641
642
643
644
        if( p_sys->b_reset_pcr )
        {
            es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
            p_sys->b_reset_pcr = false;
        }
gbazin's avatar
   
gbazin committed
645
        DemuxBlock( p_demux, packet, i_len );
646
647
648
649
650
651
        if( p_sys->i_vobu_index > 0 )
        {
            if( p_sys->i_vobu_flush == p_sys->i_vobu_index )
                DemuxForceStill( p_demux );
            p_sys->i_vobu_index++;
        }
gbazin's avatar
   
gbazin committed
652
        break;
653

gbazin's avatar
   
gbazin committed
654
655
656
    case DVDNAV_NOP:    /* Nothing */
        msg_Dbg( p_demux, "DVDNAV_NOP" );
        break;
657

gbazin's avatar
   
gbazin committed
658
659
660
    case DVDNAV_STILL_FRAME:
    {
        dvdnav_still_event_t *event = (dvdnav_still_event_t*)packet;
Laurent Aimar's avatar
Laurent Aimar committed
661
662
        bool b_still_init = false;

663
664
        vlc_mutex_lock( &p_sys->still.lock );
        if( !p_sys->still.b_enabled )
Laurent Aimar's avatar
Laurent Aimar committed
665
666
667
        {
            msg_Dbg( p_demux, "DVDNAV_STILL_FRAME" );
            msg_Dbg( p_demux, "     - length=0x%x", event->length );
668
669
            p_sys->still.b_enabled = true;

670
            if( event->length != 0xff && p_sys->still.b_created )
671
            {
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
672
                mtime_t delay = event->length * CLOCK_FREQ;
673
                vlc_timer_schedule( p_sys->still.timer, false, delay, 0 );
674
675
            }

Laurent Aimar's avatar
Laurent Aimar committed
676
677
            b_still_init = true;
        }
678
        vlc_mutex_unlock( &p_sys->still.lock );
Laurent Aimar's avatar
Laurent Aimar committed
679
680

        if( b_still_init )
681
        {
682
            DemuxForceStill( p_demux );
Laurent Aimar's avatar
Laurent Aimar committed
683
            p_sys->b_reset_pcr = true;
684
        }
685
        msleep( 40000 );
gbazin's avatar
   
gbazin committed
686
687
        break;
    }
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706

    case DVDNAV_SPU_CLUT_CHANGE:
    {
        int i;

        msg_Dbg( p_demux, "DVDNAV_SPU_CLUT_CHANGE" );
        /* Update color lookup table (16 *uint32_t in packet) */
        memcpy( p_sys->clut, packet, 16 * sizeof( uint32_t ) );

        /* HACK to get the SPU tracks registered in the right order */
        for( i = 0; i < 0x1f; i++ )
        {
            if( dvdnav_spu_stream_to_lang( p_sys->dvdnav, i ) != 0xffff )
                ESNew( p_demux, 0xbd20 + i );
        }
        /* END HACK */
        break;
    }

gbazin's avatar
   
gbazin committed
707
708
709
710
    case DVDNAV_SPU_STREAM_CHANGE:
    {
        dvdnav_spu_stream_change_event_t *event =
            (dvdnav_spu_stream_change_event_t*)packet;
711
712
        int i;

gbazin's avatar
   
gbazin committed
713
        msg_Dbg( p_demux, "DVDNAV_SPU_STREAM_CHANGE" );
Laurent Aimar's avatar
Laurent Aimar committed
714
715
        msg_Dbg( p_demux, "     - physical_wide=%d",
                 event->physical_wide );
gbazin's avatar
   
gbazin committed
716
717
718
719
        msg_Dbg( p_demux, "     - physical_letterbox=%d",
                 event->physical_letterbox);
        msg_Dbg( p_demux, "     - physical_pan_scan=%d",
                 event->physical_pan_scan );
Laurent Aimar's avatar
Laurent Aimar committed
720

721
        ESSubtitleUpdate( p_demux );
722
        p_sys->b_spu_change = true;
723
724
725
726
727
728
729
730

        /* HACK to get the SPU tracks registered in the right order */
        for( i = 0; i < 0x1f; i++ )
        {
            if( dvdnav_spu_stream_to_lang( p_sys->dvdnav, i ) != 0xffff )
                ESNew( p_demux, 0xbd20 + i );
        }
        /* END HACK */
gbazin's avatar
   
gbazin committed
731
732
        break;
    }
733

gbazin's avatar
   
gbazin committed
734
735
736
737
738
739
    case DVDNAV_AUDIO_STREAM_CHANGE:
    {
        dvdnav_audio_stream_change_event_t *event =
            (dvdnav_audio_stream_change_event_t*)packet;
        msg_Dbg( p_demux, "DVDNAV_AUDIO_STREAM_CHANGE" );
        msg_Dbg( p_demux, "     - physical=%d", event->physical );
Laurent Aimar's avatar
Laurent Aimar committed
740
        /* TODO */
gbazin's avatar
   
gbazin committed
741
742
        break;
    }
743

gbazin's avatar
   
gbazin committed
744
745
    case DVDNAV_VTS_CHANGE:
    {
Laurent Aimar's avatar
Laurent Aimar committed
746
747
        int32_t i_title = 0;
        int32_t i_part  = 0;
gbazin's avatar
   
gbazin committed
748
        int i;
Laurent Aimar's avatar
Laurent Aimar committed
749

gbazin's avatar
   
gbazin committed
750
751
752
753
754
        dvdnav_vts_change_event_t *event = (dvdnav_vts_change_event_t*)packet;
        msg_Dbg( p_demux, "DVDNAV_VTS_CHANGE" );
        msg_Dbg( p_demux, "     - vtsN=%d", event->new_vtsN );
        msg_Dbg( p_demux, "     - domain=%d", event->new_domain );

gbazin's avatar
   
gbazin committed
755
756
757
758
759
760
        /* reset PCR */
        es_out_Control( p_demux->out, ES_OUT_RESET_PCR );

        for( i = 0; i < PS_TK_COUNT; i++ )
        {
            ps_track_t *tk = &p_sys->tk[i];
761
            if( tk->b_seen )
gbazin's avatar
   
gbazin committed
762
            {
763
764
                es_format_Clean( &tk->fmt );
                if( tk->es ) es_out_Del( p_demux->out, tk->es );
gbazin's avatar
   
gbazin committed
765
            }
766
            tk->b_seen = false;
gbazin's avatar
   
gbazin committed
767
        }
768

769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
#if defined(HAVE_DVDNAV_GET_VIDEO_RESOLUTION)
        uint32_t i_width, i_height;
        if( dvdnav_get_video_resolution( p_sys->dvdnav,
                                         &i_width, &i_height ) )
            i_width = i_height = 0;
        switch( dvdnav_get_video_aspect( p_sys->dvdnav ) )
        {
        case 0:
            p_sys->sar.i_num = 4 * i_height;
            p_sys->sar.i_den = 3 * i_width;
            break;
        case 3:
            p_sys->sar.i_num = 16 * i_height;
            p_sys->sar.i_den =  9 * i_width;
            break;
        default:
            p_sys->sar.i_num = 0;
            p_sys->sar.i_den = 0;
            break;
        }
#endif

Laurent Aimar's avatar
Laurent Aimar committed
791
792
        if( dvdnav_current_title_info( p_sys->dvdnav, &i_title,
                                       &i_part ) == DVDNAV_STATUS_OK )
793
        {
Laurent Aimar's avatar
Laurent Aimar committed
794
795
796
797
798
            if( i_title >= 0 && i_title < p_sys->i_title &&
                p_demux->info.i_title != i_title )
            {
                p_demux->info.i_update |= INPUT_UPDATE_TITLE;
                p_demux->info.i_title = i_title;
799
800
            }
        }
gbazin's avatar
   
gbazin committed
801
802
        break;
    }
803

gbazin's avatar
   
gbazin committed
804
805
    case DVDNAV_CELL_CHANGE:
    {
Laurent Aimar's avatar
Laurent Aimar committed
806
807
808
        int32_t i_title = 0;
        int32_t i_part  = 0;

gbazin's avatar
   
gbazin committed
809
810
811
812
813
        dvdnav_cell_change_event_t *event =
            (dvdnav_cell_change_event_t*)packet;
        msg_Dbg( p_demux, "DVDNAV_CELL_CHANGE" );
        msg_Dbg( p_demux, "     - cellN=%d", event->cellN );
        msg_Dbg( p_demux, "     - pgN=%d", event->pgN );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
814
815
816
817
818
        msg_Dbg( p_demux, "     - cell_length=%"PRId64, event->cell_length );
        msg_Dbg( p_demux, "     - pg_length=%"PRId64, event->pg_length );
        msg_Dbg( p_demux, "     - pgc_length=%"PRId64, event->pgc_length );
        msg_Dbg( p_demux, "     - cell_start=%"PRId64, event->cell_start );
        msg_Dbg( p_demux, "     - pg_start=%"PRId64, event->pg_start );
Laurent Aimar's avatar
Laurent Aimar committed
819

820
821
        /* Store the lenght in time of the current PGC */
        p_sys->i_pgc_length = event->pgc_length / 90 * 1000;
822
823
        p_sys->i_vobu_index = 0;
        p_sys->i_vobu_flush = 0;
824

Laurent Aimar's avatar
Laurent Aimar committed
825
826
827
828
829
        /* FIXME is it correct or there is better way to know chapter change */
        if( dvdnav_current_title_info( p_sys->dvdnav, &i_title,
                                       &i_part ) == DVDNAV_STATUS_OK )
        {
            if( i_title >= 0 && i_title < p_sys->i_title &&
830
831
                i_part >= 1 && i_part <= p_sys->title[i_title]->i_seekpoint &&
                p_demux->info.i_seekpoint != i_part - 1 )
Laurent Aimar's avatar
Laurent Aimar committed
832
833
            {
                p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
834
                p_demux->info.i_seekpoint = i_part - 1;
Laurent Aimar's avatar
Laurent Aimar committed
835
836
            }
        }
gbazin's avatar
   
gbazin committed
837
838
        break;
    }
Laurent Aimar's avatar
Laurent Aimar committed
839

gbazin's avatar
   
gbazin committed
840
841
    case DVDNAV_NAV_PACKET:
    {
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
        p_sys->i_vobu_index = 1;
        p_sys->i_vobu_flush = 0;

        /* Look if we have need to force a flush (and when) */
        const pci_gi_t *p_pci_gi = &dvdnav_get_current_nav_pci( p_sys->dvdnav )->pci_gi;
        if( p_pci_gi->vobu_se_e_ptm != 0 && p_pci_gi->vobu_se_e_ptm < p_pci_gi->vobu_e_ptm )
        {
            const dsi_gi_t *p_dsi_gi = &dvdnav_get_current_nav_dsi( p_sys->dvdnav )->dsi_gi;
            if( p_dsi_gi->vobu_3rdref_ea != 0 )
                p_sys->i_vobu_flush = p_dsi_gi->vobu_3rdref_ea;
            else if( p_dsi_gi->vobu_2ndref_ea != 0 )
                p_sys->i_vobu_flush = p_dsi_gi->vobu_2ndref_ea;
            else if( p_dsi_gi->vobu_1stref_ea != 0 )
                p_sys->i_vobu_flush = p_dsi_gi->vobu_1stref_ea;
        }

858
#ifdef DVDNAV_DEBUG
gbazin's avatar
   
gbazin committed
859
        msg_Dbg( p_demux, "DVDNAV_NAV_PACKET" );
860
#endif
gbazin's avatar
   
gbazin committed
861
862
863
864
865
866
        /* A lot of thing to do here :
         *  - handle packet
         *  - fetch pts (for time display)
         *  - ...
         */
        DemuxBlock( p_demux, packet, i_len );
867
        if( p_sys->b_spu_change )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
868
        {
869
870
            ButtonUpdate( p_demux, false );
            p_sys->b_spu_change = false;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
871
        }
gbazin's avatar
   
gbazin committed
872
873
        break;
    }
874

gbazin's avatar
   
gbazin committed
875
876
    case DVDNAV_STOP:   /* EOF */
        msg_Dbg( p_demux, "DVDNAV_STOP" );
gbazin's avatar
gbazin committed
877
878
879
880

#if DVD_READ_CACHE
        dvdnav_free_cache_block( p_sys->dvdnav, packet );
#endif
gbazin's avatar
   
gbazin committed
881
        return 0;
882

gbazin's avatar
   
gbazin committed
883
884
885
886
887
888
    case DVDNAV_HIGHLIGHT:
    {
        dvdnav_highlight_event_t *event = (dvdnav_highlight_event_t*)packet;
        msg_Dbg( p_demux, "DVDNAV_HIGHLIGHT" );
        msg_Dbg( p_demux, "     - display=%d", event->display );
        msg_Dbg( p_demux, "     - buttonN=%d", event->buttonN );
889
        ButtonUpdate( p_demux, false );
gbazin's avatar
   
gbazin committed
890
891
        break;
    }
892

gbazin's avatar
   
gbazin committed
893
894
    case DVDNAV_HOP_CHANNEL:
        msg_Dbg( p_demux, "DVDNAV_HOP_CHANNEL" );
895
896
        p_sys->i_vobu_index = 0;
        p_sys->i_vobu_flush = 0;
Laurent Aimar's avatar
Laurent Aimar committed
897
        es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
gbazin's avatar
   
gbazin committed
898
        break;
899

gbazin's avatar
   
gbazin committed
900
901
    case DVDNAV_WAIT:
        msg_Dbg( p_demux, "DVDNAV_WAIT" );
902

Laurent Aimar's avatar
Laurent Aimar committed
903
904
905
906
907
908
909
910
911
912
913
        bool b_empty;
        es_out_Control( p_demux->out, ES_OUT_GET_EMPTY, &b_empty );
        if( !b_empty )
        {
            msleep( 40*1000 );
        }
        else
        {
            dvdnav_wait_skip( p_sys->dvdnav );
            p_sys->b_reset_pcr = true;
        }
gbazin's avatar
   
gbazin committed
914
        break;
915

gbazin's avatar
   
gbazin committed
916
917
918
    default:
        msg_Warn( p_demux, "Unknown event (0x%x)", i_event );
        break;
919
    }
gbazin's avatar
   
gbazin committed
920

921
922
923
924
#if DVD_READ_CACHE
    dvdnav_free_cache_block( p_sys->dvdnav, packet );
#endif

925
926
    return 1;
}
Laurent Aimar's avatar
Laurent Aimar committed
927

928
929
930
/* Get a 2 char code
 * FIXME: partiallyy duplicated from src/input/es_out.c
 */
Laurent Aimar's avatar
Laurent Aimar committed
931
static char *DemuxGetLanguageCode( demux_t *p_demux, const char *psz_var )
932
933
934
935
936
937
{
    const iso639_lang_t *pl;
    char *psz_lang;
    char *p;

    psz_lang = var_CreateGetString( p_demux, psz_var );
938
939
940
    if( !psz_lang )
        return strdup(LANGUAGE_DEFAULT);

941
942
    /* XXX: we will use only the first value
     * (and ignore other ones in case of a list) */
943
944
    if( ( p = strchr( psz_lang, ',' ) ) )
        *p = '\0';
945

946
    for( pl = p_languages; pl->psz_eng_name != NULL; pl++ )
947
    {
948
949
        if( *psz_lang == '\0' )
            continue;
950
951
952