dvdnav.c 43.7 KB
Newer Older
1
/*****************************************************************************
gbazin's avatar
 
gbazin committed
2
 * dvdnav.c: DVD module using the dvdnav library.
3
 *****************************************************************************
4
 * Copyright (C) 2004 the VideoLAN team
5
 * $Id$
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23 24 25 26 27 28
 *****************************************************************************/

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

#include <vlc/vlc.h>
zorglub's avatar
zorglub committed
29 30 31 32 33
#include <vlc_input.h>
#include <vlc_access.h>
#include <vlc_demux.h>

#include <vlc_interface.h>
34

35 36 37
#ifdef HAVE_UNISTD_H
#   include <unistd.h>
#endif
38 39 40 41 42 43 44 45 46 47
#ifdef HAVE_SYS_TYPES_H
#   include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#   include <sys/stat.h>
#endif
#ifdef HAVE_FCNTL_H
#   include <fcntl.h>
#endif

48 49 50
#include "vlc_keys.h"
#include "iso_lang.h"

51
/* FIXME we should find a better way than including that */
52
#include "../../src/text/iso-639_def.h"
53 54


55 56
#include <dvdnav/dvdnav.h>

57
#include "../demux/ps.h"
58 59 60 61

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
62 63
#define ANGLE_TEXT N_("DVD angle")
#define ANGLE_LONGTEXT N_( \
zorglub's avatar
zorglub committed
64
     "Default DVD angle." )
65

66
#define CACHING_TEXT N_("Caching value in ms")
67
#define CACHING_LONGTEXT N_( \
68
    "Caching value for DVDs. This "\
zorglub's avatar
zorglub committed
69
    "value should be set in milliseconds." )
70 71
#define MENU_TEXT N_("Start directly in menu")
#define MENU_LONGTEXT N_( \
zorglub's avatar
zorglub committed
72 73
    "Start the DVD directly in the main menu. This "\
    "will try to skip all the useless warning introductions." )
74

75 76
#define LANGUAGE_DEFAULT ("en")

Laurent Aimar's avatar
Laurent Aimar committed
77 78
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );
79 80

vlc_module_begin();
zorglub's avatar
zorglub committed
81
    set_shortname( _("DVD with menus") );
hartman's avatar
hartman committed
82
    set_description( _("DVDnav Input") );
zorglub's avatar
zorglub committed
83 84
    set_category( CAT_INPUT );
    set_subcategory( SUBCAT_INPUT_ACCESS );
85 86
    add_integer( "dvdnav-angle", 1, NULL, ANGLE_TEXT,
        ANGLE_LONGTEXT, VLC_FALSE );
hartman's avatar
hartman committed
87 88
    add_integer( "dvdnav-caching", DEFAULT_PTS_DELAY / 1000, NULL,
        CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
89 90
    add_bool( "dvdnav-menu", VLC_TRUE, NULL,
        MENU_TEXT, MENU_LONGTEXT, VLC_FALSE );
91
    set_capability( "access_demux", 5 );
92
    add_shortcut( "dvd" );
93
    add_shortcut( "dvdnav" );
Laurent Aimar's avatar
Laurent Aimar committed
94
    set_callbacks( Open, Close );
95 96
vlc_module_end();

97 98 99
/* Shall we use libdvdnav's read ahead cache? */
#define DVD_READ_CACHE 1

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
typedef struct
{
    VLC_COMMON_MEMBERS

    demux_t        *p_demux;
    vlc_mutex_t     lock;

    vlc_bool_t      b_moved;
    vlc_bool_t      b_clicked;
    vlc_bool_t      b_key;

    vlc_bool_t      b_still;
    int64_t         i_still_end;
gbazin's avatar
 
gbazin committed
116

117 118
} event_thread_t;

gbazin's avatar
 
gbazin committed
119
static int EventThread( vlc_object_t * );
120 121 122 123 124 125 126

struct demux_sys_t
{
    dvdnav_t    *dvdnav;

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

    /* for spu variables */
    input_thread_t *p_input;

    /* event */
    event_thread_t *p_ev;

135 136 137
    /* palette for menus */
    uint32_t clut[16];
    uint8_t  palette[4][4];
138
    vlc_bool_t b_spu_change;
139 140 141 142

    /* */
    int i_aspect;

Laurent Aimar's avatar
Laurent Aimar committed
143 144
    int           i_title;
    input_title_t **title;
145 146 147

    /* lenght of program group chain */
    mtime_t     i_pgc_length;
148 149
};

Laurent Aimar's avatar
Laurent Aimar committed
150
static int Control( demux_t *, int, va_list );
151 152
static int Demux( demux_t * );
static int DemuxBlock( demux_t *, uint8_t *, int );
153

154
static void DemuxTitles( demux_t * );
Laurent Aimar's avatar
Laurent Aimar committed
155
static void ESSubtitleUpdate( demux_t * );
156
static void ButtonUpdate( demux_t *, vlc_bool_t );
Laurent Aimar's avatar
Laurent Aimar committed
157

158
static void ESNew( demux_t *, int );
159 160
static int ProbeDVD( demux_t *, char * );

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

163 164 165
/*****************************************************************************
 * DemuxOpen:
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
166
static int Open( vlc_object_t *p_this )
167
{
gbazin's avatar
 
gbazin committed
168 169
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys;
170
    dvdnav_t    *p_dvdnav;
171
    int         i_angle;
gbazin's avatar
 
gbazin committed
172
    char        *psz_name;
173
    char        *psz_code;
174
    vlc_value_t val;
175

176
    if( !p_demux->psz_path || !*p_demux->psz_path )
177
    {
178 179 180 181
        /* Only when selected */
        if( !p_this->b_force ) return VLC_EGENERIC;

        psz_name = var_CreateGetString( p_this, "dvd" );
182
        if( !psz_name )
183
        {
184
            psz_name = strdup("");
185
        }
186
    }
187 188
    else
        psz_name = strdup( p_demux->psz_path );
189 190 191 192 193

#ifdef WIN32
    if( psz_name[0] && psz_name[1] == ':' &&
        psz_name[2] == '\\' && psz_name[3] == '\0' ) psz_name[2] = '\0';
#endif
194

195 196 197 198 199 200 201
    /* Try some simple probing to avoid going through dvdnav_open too often */
    if( ProbeDVD( p_demux, psz_name ) != VLC_SUCCESS )
    {
        free( psz_name );
        return VLC_EGENERIC;
    }

202 203
    /* Open dvdnav */
    if( dvdnav_open( &p_dvdnav, psz_name ) != DVDNAV_STATUS_OK )
gbazin's avatar
 
gbazin committed
204
    {
205 206
        msg_Warn( p_demux, "cannot open dvdnav" );
        free( psz_name );
gbazin's avatar
 
gbazin committed
207 208
        return VLC_EGENERIC;
    }
209
    free( psz_name );
gbazin's avatar
 
gbazin committed
210

211
    /* Fill p_demux field */
212
    STANDARD_DEMUX_INIT; p_sys = p_demux->p_sys;
213
    p_sys->dvdnav = p_dvdnav;
214 215 216

    ps_track_init( p_sys->tk );
    p_sys->i_aspect = -1;
217
    p_sys->i_mux_rate = 0;
218
    p_sys->i_pgc_length = 0;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
219
    p_sys->b_spu_change = VLC_FALSE;
220

221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
    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 );
    }

238
    /* Configure dvdnav */
239 240
    if( dvdnav_set_readahead_flag( p_sys->dvdnav, DVD_READ_CACHE ) !=
          DVDNAV_STATUS_OK )
241
    {
hartman's avatar
hartman committed
242
        msg_Warn( p_demux, "cannot set read-a-head flag" );
243
    }
gbazin's avatar
 
gbazin committed
244 245 246

    if( dvdnav_set_PGC_positioning_flag( p_sys->dvdnav, 1 ) !=
          DVDNAV_STATUS_OK )
247 248 249
    {
        msg_Warn( p_demux, "cannot set PGC positioning flag" );
    }
gbazin's avatar
 
gbazin committed
250

251
    /* Set menu language ("en")
252 253
     * XXX: maybe it would be better to set it like audio/spu
     * or to create a --menu-language option */
hartman's avatar
hartman committed
254
    if( dvdnav_menu_language_select( p_sys->dvdnav, LANGUAGE_DEFAULT ) !=
255
        DVDNAV_STATUS_OK )
256
    {
257 258
        msg_Warn( p_demux, "can't set menu language to '%s' (%s)",
                  LANGUAGE_DEFAULT, dvdnav_err_to_string( p_sys->dvdnav ) );
259 260 261 262
    }

    /* Set audio language */
    psz_code = DemuxGetLanguageCode( p_demux, "audio-language" );
hartman's avatar
hartman committed
263
    if( dvdnav_audio_language_select( p_sys->dvdnav, psz_code ) !=
264
        DVDNAV_STATUS_OK )
265
    {
266 267
        msg_Warn( p_demux, "can't set audio language to '%s' (%s)",
                  psz_code, dvdnav_err_to_string( p_sys->dvdnav ) );
268 269
        /* We try to fall back to 'en' */
        if( strcmp( psz_code, LANGUAGE_DEFAULT ) )
hartman's avatar
hartman committed
270
            dvdnav_audio_language_select( p_sys->dvdnav, LANGUAGE_DEFAULT );
271 272 273 274
    }
    free( psz_code );

    /* Set spu language */
hartman's avatar
hartman committed
275 276
    psz_code = DemuxGetLanguageCode( p_demux, "sub-language" );
    if( dvdnav_spu_language_select( p_sys->dvdnav, psz_code ) !=
277
        DVDNAV_STATUS_OK )
278
    {
279 280
        msg_Warn( p_demux, "can't set spu language to '%s' (%s)",
                  psz_code, dvdnav_err_to_string( p_sys->dvdnav ) );
281 282 283
        /* We try to fall back to 'en' */
        if( strcmp( psz_code, LANGUAGE_DEFAULT ) )
            dvdnav_spu_language_select(p_sys->dvdnav, LANGUAGE_DEFAULT );
284
    }
285
    free( psz_code );
286

Laurent Aimar's avatar
Laurent Aimar committed
287 288
    DemuxTitles( p_demux );

289 290
    var_Create( p_demux, "dvdnav-menu", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
    var_Get( p_demux, "dvdnav-menu", &val );
291
    if( val.b_bool )
292 293 294 295 296
    {
        msg_Dbg( p_demux, "trying to go to dvd menu" );

        if( dvdnav_title_play( p_sys->dvdnav, 1 ) != DVDNAV_STATUS_OK )
        {
297
            msg_Err( p_demux, "cannot set title (can't decrypt DVD?)" );
298 299 300
            intf_UserFatal( p_demux, VLC_FALSE, _("Playback failure"), 
                            _("VLC cannot set the DVD's title. It possibly "
                              "cannot decrypt the entire disk.") );
301 302 303
            dvdnav_close( p_sys->dvdnav );
            free( p_sys );
            return VLC_EGENERIC;
304 305 306 307 308
        }

        if( dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Title ) !=
            DVDNAV_STATUS_OK )
        {
309 310 311 312
            /* 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" );
313 314 315
        }
    }

316 317 318 319
    var_Create( p_demux, "dvdnav-angle", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
    var_Get( p_demux, "dvdnav-angle", &val );
    i_angle = val.i_int > 0 ? val.i_int : 1;

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

323
    /* FIXME hack hack hack hack FIXME */
324 325 326 327 328 329 330
    /* Get p_input and create variable */
    p_sys->p_input = vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT );
    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 );
331
    var_Create( p_sys->p_input, "menu-palette", VLC_VAR_ADDRESS );
332 333 334 335 336 337 338 339 340
    var_Create( p_sys->p_input, "highlight", VLC_VAR_BOOL );
    var_Create( p_sys->p_input, "highlight-mutex", VLC_VAR_MUTEX );

    /* Now create our event thread catcher */
    p_sys->p_ev = vlc_object_create( p_demux, sizeof( event_thread_t ) );
    p_sys->p_ev->p_demux = p_demux;
    vlc_thread_create( p_sys->p_ev, "dvdnav event thread handler", EventThread,
                       VLC_THREAD_PRIORITY_LOW, VLC_FALSE );

341 342 343 344
    return VLC_SUCCESS;
}

/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
345
 * Close:
346
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
347
static void Close( vlc_object_t *p_this )
348 349 350
{
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys = p_demux->p_sys;
351
    int i;
352

353
    /* stop the event handler */
354
    vlc_object_kill( p_sys->p_ev );
355 356 357 358 359 360 361 362 363 364
    vlc_thread_join( p_sys->p_ev );
    vlc_object_destroy( p_sys->p_ev );

    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" );
365
    var_Destroy( p_sys->p_input, "menu-palette" );
366 367

    vlc_object_release( p_sys->p_input );
368

369 370 371 372 373 374 375 376 377 378
    for( i = 0; i < PS_TK_COUNT; i++ )
    {
        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 );
        }
    }

379 380 381 382 383
    dvdnav_close( p_sys->dvdnav );
    free( p_sys );
}

/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
384
 * Control:
385
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
386
static int Control( demux_t *p_demux, int i_query, va_list args )
387 388 389
{
    demux_sys_t *p_sys = p_demux->p_sys;
    double f, *pf;
Laurent Aimar's avatar
Laurent Aimar committed
390 391 392 393 394
    vlc_bool_t *pb;
    int64_t *pi64;
    input_title_t ***ppp_title;
    int          *pi_int;
    int i;
395 396 397

    switch( i_query )
    {
398
        case DEMUX_SET_POSITION:
399
        case DEMUX_GET_POSITION:
400 401
        case DEMUX_GET_TIME:
        case DEMUX_GET_LENGTH:
402 403
        {
            uint32_t pos, len;
404 405
            if( dvdnav_get_position( p_sys->dvdnav, &pos, &len ) !=
                  DVDNAV_STATUS_OK || len == 0 )
406
            {
407
                return VLC_EGENERIC;
408
            }
409 410

            if( i_query == DEMUX_GET_POSITION )
411
            {
412 413 414
                pf = (double*)va_arg( args, double* );
                *pf = (double)pos / (double)len;
                return VLC_SUCCESS;
415
            }
416
            else if( i_query == DEMUX_SET_POSITION )
417
            {
418
                f = (double)va_arg( args, double );
419
                pos = f * len;
gbazin's avatar
 
gbazin committed
420 421
                if( dvdnav_sector_search( p_sys->dvdnav, pos, SEEK_SET ) ==
                      DVDNAV_STATUS_OK )
422 423 424 425
                {
                    return VLC_SUCCESS;
                }
            }
426 427 428
            else if( i_query == DEMUX_GET_TIME )
            {
                pi64 = (int64_t*)va_arg( args, int64_t * );
429
                if( p_sys->i_pgc_length > 0 )
430
                {
431
                    *pi64 = p_sys->i_pgc_length * pos / len;
432 433 434 435 436 437
                    return VLC_SUCCESS;
                }
            }
            else if( i_query == DEMUX_GET_LENGTH )
            {
                pi64 = (int64_t*)va_arg( args, int64_t * );
438
                if( p_sys->i_pgc_length > 0 )
439
                {
440
                    *pi64 = (int64_t)p_sys->i_pgc_length;
441 442 443
                    return VLC_SUCCESS;
                }
            }
444

445 446 447
            return VLC_EGENERIC;
        }

Laurent Aimar's avatar
Laurent Aimar committed
448 449 450 451 452 453 454 455 456 457 458 459 460 461
        /* Special for access_demux */
        case DEMUX_CAN_PAUSE:
        case DEMUX_CAN_CONTROL_PACE:
            /* TODO */
            pb = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
            *pb = VLC_TRUE;
            return VLC_SUCCESS;

        case DEMUX_SET_PAUSE_STATE:
            return VLC_SUCCESS;

        case DEMUX_GET_TITLE_INFO:
            ppp_title = (input_title_t***)va_arg( args, input_title_t*** );
            pi_int    = (int*)va_arg( args, int* );
462 463
            *((int*)va_arg( args, int* )) = 0; /* Title offset */
            *((int*)va_arg( args, int* )) = 1; /* Chapter offset */
Laurent Aimar's avatar
Laurent Aimar committed
464 465 466 467 468 469 470 471 472 473 474 475

            /* Duplicate title infos */
            *pi_int = p_sys->i_title;
            *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 );
476 477 478 479
            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
480 481 482 483
            {
                msg_Warn( p_demux, "cannot set title/chapter" );
                return VLC_EGENERIC;
            }
484 485
            p_demux->info.i_update |=
                INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
Laurent Aimar's avatar
Laurent Aimar committed
486 487 488 489 490 491 492 493 494 495 496 497
            p_demux->info.i_title = i;
            p_demux->info.i_seekpoint = 0;
            return VLC_SUCCESS;

        case DEMUX_SET_SEEKPOINT:
            i = (int)va_arg( args, int );
            if( p_demux->info.i_title == 0 )
            {
                int i_ret;
                /* Special case */
                switch( i )
                {
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
                case 0:
                    i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Escape );
                    break;
                case 1:
                    i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Root );
                    break;
                case 2:
                    i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Title );
                    break;
                case 3:
                    i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Part );
                    break;
                case 4:
                    i_ret = dvdnav_menu_call( p_sys->dvdnav,
                                              DVD_MENU_Subpicture );
                    break;
                case 5:
                    i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Audio );
                    break;
                case 6:
                    i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Angle );
                    break;
                default:
                    return VLC_EGENERIC;
Laurent Aimar's avatar
Laurent Aimar committed
522
                }
523

Laurent Aimar's avatar
Laurent Aimar committed
524 525 526
                if( i_ret != DVDNAV_STATUS_OK )
                    return VLC_EGENERIC;
            }
527
            else if( dvdnav_part_play( p_sys->dvdnav, p_demux->info.i_title,
528
                                       i + 1 ) != DVDNAV_STATUS_OK )
Laurent Aimar's avatar
Laurent Aimar committed
529 530 531 532 533 534 535 536 537 538
            {
                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:
            pi64 = (int64_t*)va_arg( args, int64_t * );
539
            *pi64 = (int64_t)var_GetInteger( p_demux, "dvdnav-caching" ) *1000;
Laurent Aimar's avatar
Laurent Aimar committed
540 541
            return VLC_SUCCESS;

542 543 544 545 546 547 548
        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]) )
            {
549
                vlc_meta_t *p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t* );
550
                vlc_meta_Set( p_meta, vlc_meta_Title, title_name );
551 552 553 554 555
                return VLC_SUCCESS;
            }
            return VLC_EGENERIC;
        }

556 557 558 559 560 561 562
        /* TODO implement others */
        default:
            return VLC_EGENERIC;
    }
}

/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
563
 * Demux:
564
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
565
static int Demux( demux_t *p_demux )
566 567 568
{
    demux_sys_t *p_sys = p_demux->p_sys;

569 570
    uint8_t buffer[DVD_VIDEO_LB_LEN];
    uint8_t *packet = buffer;
571 572 573
    int i_event;
    int i_len;

574
#if DVD_READ_CACHE
575 576
    if( dvdnav_get_next_cache_block( p_sys->dvdnav, &packet, &i_event, &i_len )
        == DVDNAV_STATUS_ERR )
577
#else
578 579
    if( dvdnav_get_next_block( p_sys->dvdnav, packet, &i_event, &i_len )
        == DVDNAV_STATUS_ERR )
580
#endif
581 582 583 584 585
    {
        msg_Warn( p_demux, "cannot get next block (%s)",
                  dvdnav_err_to_string( p_sys->dvdnav ) );
        return -1;
    }
gbazin's avatar
 
gbazin committed
586

587 588
    switch( i_event )
    {
gbazin's avatar
 
gbazin committed
589 590 591
    case DVDNAV_BLOCK_OK:   /* mpeg block */
        DemuxBlock( p_demux, packet, i_len );
        break;
592

gbazin's avatar
 
gbazin committed
593 594 595
    case DVDNAV_NOP:    /* Nothing */
        msg_Dbg( p_demux, "DVDNAV_NOP" );
        break;
596

gbazin's avatar
 
gbazin committed
597 598 599
    case DVDNAV_STILL_FRAME:
    {
        dvdnav_still_event_t *event = (dvdnav_still_event_t*)packet;
600 601
        vlc_mutex_lock( &p_sys->p_ev->lock );
        if( !p_sys->p_ev->b_still )
602
        {
603 604 605 606
            msg_Dbg( p_demux, "DVDNAV_STILL_FRAME" );
            msg_Dbg( p_demux, "     - length=0x%x", event->length );
            p_sys->p_ev->b_still = VLC_TRUE;
            if( event->length == 0xff )
607
            {
608 609 610 611 612 613
                p_sys->p_ev->i_still_end = 0;
            }
            else
            {
                p_sys->p_ev->i_still_end = (int64_t)event->length *
                    1000000 + mdate() + p_sys->p_input->i_pts_delay;
614 615
            }
        }
616 617
        vlc_mutex_unlock( &p_sys->p_ev->lock );
        msleep( 40000 );
gbazin's avatar
 
gbazin committed
618 619
        break;
    }
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638

    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
639 640 641 642
    case DVDNAV_SPU_STREAM_CHANGE:
    {
        dvdnav_spu_stream_change_event_t *event =
            (dvdnav_spu_stream_change_event_t*)packet;
643 644
        int i;

gbazin's avatar
 
gbazin committed
645
        msg_Dbg( p_demux, "DVDNAV_SPU_STREAM_CHANGE" );
Laurent Aimar's avatar
Laurent Aimar committed
646 647
        msg_Dbg( p_demux, "     - physical_wide=%d",
                 event->physical_wide );
gbazin's avatar
 
gbazin committed
648 649 650 651
        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
652

653
        ESSubtitleUpdate( p_demux );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
654
        p_sys->b_spu_change = VLC_TRUE;
655 656 657 658 659 660 661 662

        /* 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
663 664
        break;
    }
665

gbazin's avatar
 
gbazin committed
666 667 668 669 670 671
    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
672
        /* TODO */
gbazin's avatar
 
gbazin committed
673 674
        break;
    }
675

gbazin's avatar
 
gbazin committed
676 677
    case DVDNAV_VTS_CHANGE:
    {
Laurent Aimar's avatar
Laurent Aimar committed
678 679
        int32_t i_title = 0;
        int32_t i_part  = 0;
gbazin's avatar
 
gbazin committed
680
        int i;
Laurent Aimar's avatar
Laurent Aimar committed
681

gbazin's avatar
 
gbazin committed
682 683 684 685 686 687
        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 );

        /* dvdnav_get_video_aspect / dvdnav_get_video_scale_permission */
688
        /* TODO check if we always have VTS and CELL */
gbazin's avatar
 
gbazin committed
689
        p_sys->i_aspect = dvdnav_get_video_aspect( p_sys->dvdnav );
gbazin's avatar
 
gbazin committed
690 691 692 693 694 695 696

        /* 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];
697
            if( tk->b_seen )
gbazin's avatar
 
gbazin committed
698
            {
699 700
                es_format_Clean( &tk->fmt );
                if( tk->es ) es_out_Del( p_demux->out, tk->es );
gbazin's avatar
 
gbazin committed
701 702 703
            }
            tk->b_seen = VLC_FALSE;
        }
704

Laurent Aimar's avatar
Laurent Aimar committed
705 706
        if( dvdnav_current_title_info( p_sys->dvdnav, &i_title,
                                       &i_part ) == DVDNAV_STATUS_OK )
707
        {
Laurent Aimar's avatar
Laurent Aimar committed
708 709 710 711 712
            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;
713 714
            }
        }
gbazin's avatar
 
gbazin committed
715 716
        break;
    }
717

gbazin's avatar
 
gbazin committed
718 719
    case DVDNAV_CELL_CHANGE:
    {
Laurent Aimar's avatar
Laurent Aimar committed
720 721 722
        int32_t i_title = 0;
        int32_t i_part  = 0;

gbazin's avatar
 
gbazin committed
723 724 725 726 727
        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 );
728 729 730 731 732
        msg_Dbg( p_demux, "     - cell_length="I64Fd, event->cell_length );
        msg_Dbg( p_demux, "     - pg_length="I64Fd, event->pg_length );
        msg_Dbg( p_demux, "     - pgc_length="I64Fd, event->pgc_length );
        msg_Dbg( p_demux, "     - cell_start="I64Fd, event->cell_start );
        msg_Dbg( p_demux, "     - pg_start="I64Fd, event->pg_start );
Laurent Aimar's avatar
Laurent Aimar committed
733

734 735 736
        /* Store the lenght in time of the current PGC */
        p_sys->i_pgc_length = event->pgc_length / 90 * 1000;

Laurent Aimar's avatar
Laurent Aimar committed
737 738 739 740 741
        /* 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 &&
742 743
                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
744 745
            {
                p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
746
                p_demux->info.i_seekpoint = i_part - 1;
Laurent Aimar's avatar
Laurent Aimar committed
747 748
            }
        }
gbazin's avatar
 
gbazin committed
749 750
        break;
    }
Laurent Aimar's avatar
Laurent Aimar committed
751

gbazin's avatar
 
gbazin committed
752 753
    case DVDNAV_NAV_PACKET:
    {
754
#ifdef DVDNAV_DEBUG
gbazin's avatar
 
gbazin committed
755
        msg_Dbg( p_demux, "DVDNAV_NAV_PACKET" );
756
#endif
gbazin's avatar
 
gbazin committed
757 758 759 760 761 762
        /* A lot of thing to do here :
         *  - handle packet
         *  - fetch pts (for time display)
         *  - ...
         */
        DemuxBlock( p_demux, packet, i_len );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
763
        if( p_sys->b_spu_change ) 
Jean-Paul Saman's avatar
Jean-Paul Saman committed
764 765 766 767
        {
            ButtonUpdate( p_demux, VLC_FALSE );
            p_sys->b_spu_change = VLC_FALSE;
        }
gbazin's avatar
 
gbazin committed
768 769
        break;
    }
770

gbazin's avatar
 
gbazin committed
771 772
    case DVDNAV_STOP:   /* EOF */
        msg_Dbg( p_demux, "DVDNAV_STOP" );
gbazin's avatar
gbazin committed
773 774 775 776

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

gbazin's avatar
 
gbazin committed
779 780 781 782 783 784
    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 );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
785
        ButtonUpdate( p_demux, VLC_FALSE );
gbazin's avatar
 
gbazin committed
786 787
        break;
    }
788

gbazin's avatar
 
gbazin committed
789 790 791 792
    case DVDNAV_HOP_CHANNEL:
        msg_Dbg( p_demux, "DVDNAV_HOP_CHANNEL" );
        /* We should try to flush all our internal buffer */
        break;
793

gbazin's avatar
 
gbazin committed
794 795
    case DVDNAV_WAIT:
        msg_Dbg( p_demux, "DVDNAV_WAIT" );
796

797 798
        /* reset PCR */
        es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
gbazin's avatar
 
gbazin committed
799 800
        dvdnav_wait_skip( p_sys->dvdnav );
        break;
801

gbazin's avatar
 
gbazin committed
802 803 804
    default:
        msg_Warn( p_demux, "Unknown event (0x%x)", i_event );
        break;
805
    }
gbazin's avatar
 
gbazin committed
806

807 808 809 810
#if DVD_READ_CACHE
    dvdnav_free_cache_block( p_sys->dvdnav, packet );
#endif

811 812
    return 1;
}
Laurent Aimar's avatar
Laurent Aimar committed
813

814 815 816
/* Get a 2 char code
 * FIXME: partiallyy duplicated from src/input/es_out.c
 */
Laurent Aimar's avatar
Laurent Aimar committed
817
static char *DemuxGetLanguageCode( demux_t *p_demux, const char *psz_var )
818 819 820 821 822 823
{
    const iso639_lang_t *pl;
    char *psz_lang;
    char *p;

    psz_lang = var_CreateGetString( p_demux, psz_var );
824 825 826
    /* XXX: we will use only the first value
     * (and ignore other ones in case of a list) */
    if( ( p = strchr( psz_lang, ',' ) ) ) *p = '\0';
827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845

    for( pl = p_languages; pl->psz_iso639_1 != NULL; pl++ )
    {
        if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||
            !strcasecmp( pl->psz_native_name, psz_lang ) ||
            !strcasecmp( pl->psz_iso639_1, psz_lang ) ||
            !strcasecmp( pl->psz_iso639_2T, psz_lang ) ||
            !strcasecmp( pl->psz_iso639_2B, psz_lang ) )
            break;
    }

    free( psz_lang );

    if( pl->psz_iso639_1 != NULL )
        return strdup( pl->psz_iso639_1 );

    return strdup(LANGUAGE_DEFAULT);
}

Laurent Aimar's avatar
Laurent Aimar committed
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
static void DemuxTitles( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    input_title_t *t;
    seekpoint_t *s;
    int32_t i_titles;
    int i;

    /* Menu */
    t = vlc_input_title_New();
    t->b_menu = VLC_TRUE;
    t->psz_name = strdup( "DVD Menu" );

    s = vlc_seekpoint_New();
    s->psz_name = strdup( "Resume" );
    TAB_APPEND( t->i_seekpoint, t->seekpoint, s );

    s = vlc_seekpoint_New();
    s->psz_name = strdup( "Root" );
    TAB_APPEND( t->i_seekpoint, t->seekpoint, s );

    s = vlc_seekpoint_New();
    s->psz_name = strdup( "Title" );
    TAB_APPEND( t->i_seekpoint, t->seekpoint, s );

    s = vlc_seekpoint_New();
    s->psz_name = strdup( "Chapter" );
    TAB_APPEND( t->i_seekpoint, t->seekpoint, s );

    s = vlc_seekpoint_New();
    s->psz_name = strdup( "Subtitle" );
    TAB_APPEND( t->i_seekpoint, t->seekpoint, s );

    s = vlc_seekpoint_New();
    s->psz_name = strdup( "Audio" );
    TAB_APPEND( t->i_seekpoint, t->seekpoint, s );

    s = vlc_seekpoint_New();
    s->psz_name = strdup( "Angle" );
    TAB_APPEND( t->i_seekpoint, t->seekpoint, s );

    TAB_APPEND( p_sys->i_title, p_sys->title, t );

    /* Find out number of titles/chapters */
    dvdnav_get_number_of_titles( p_sys->dvdnav, &i_titles );
891
    for( i = 1; i <= i_titles; i++ )
Laurent Aimar's avatar
Laurent Aimar committed
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
    {
        int32_t i_chapters = 0;
        int j;

        dvdnav_get_number_of_parts( p_sys->dvdnav, i, &i_chapters );

        t = vlc_input_title_New();
        for( j = 0; j < __MAX( i_chapters, 1 ); j++ )
        {
            s = vlc_seekpoint_New();
            TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
        }

        TAB_APPEND( p_sys->i_title, p_sys->title, t );
    }
}

Laurent Aimar's avatar
Laurent Aimar committed
909 910 911
/*****************************************************************************
 * Update functions:
 *****************************************************************************/
912
static void ButtonUpdate( demux_t *p_demux, vlc_bool_t b_mode )
Laurent Aimar's avatar
Laurent Aimar committed
913 914 915 916 917 918 919 920 921 922 923 924 925
{
    demux_sys_t *p_sys = p_demux->p_sys;
    vlc_value_t val;
    int32_t i_title, i_part;

    dvdnav_current_title_info( p_sys->dvdnav, &i_title, &i_part );

    if( var_Get( p_sys->p_input, "highlight-mutex", &val ) == VLC_SUCCESS )
    {
        vlc_mutex_t *p_mutex = val.p_address;
        dvdnav_highlight_area_t hl;
        int32_t i_button;

926 927
        if( dvdnav_get_current_highlight( p_sys->dvdnav, &i_button )
            != DVDNAV_STATUS_OK )
Laurent Aimar's avatar
Laurent Aimar committed
928 929 930 931 932 933 934
        {
            msg_Err( p_demux, "dvdnav_get_current_highlight failed" );
            return;
        }

        if( i_button > 0 && i_title ==  0 )
        {
935
            int i;
Laurent Aimar's avatar
Laurent Aimar committed
936 937
            pci_t *pci = dvdnav_get_current_nav_pci( p_sys->dvdnav );

938
            dvdnav_get_highlight_area( pci, i_button, b_mode, &hl );
Laurent Aimar's avatar
Laurent Aimar committed
939

940 941 942 943 944 945 946 947 948 949 950
            for( i = 0; i < 4; i++ )
            {
                uint32_t i_yuv = p_sys->clut[(hl.palette>>(16+i*4))&0x0f];
                uint8_t i_alpha = (hl.palette>>(i*4))&0x0f;
                i_alpha = i_alpha == 0xf ? 0xff : i_alpha << 4;

                p_sys->palette[i][0] = (i_yuv >> 16) & 0xff;
                p_sys->palette[i][1] = (i_yuv >> 0) & 0xff;
                p_sys->palette[i][2] = (i_yuv >> 8) & 0xff;
                p_sys->palette[i][3] = i_alpha;
            }
Laurent Aimar's avatar
Laurent Aimar committed
951 952 953 954 955 956 957

            vlc_mutex_lock( p_mutex );
            val.i_int = hl.sx; var_Set( p_sys->p_input, "x-start", val );
            val.i_int = hl.ex; var_Set( p_sys->p_input, "x-end", val );
            val.i_int = hl.sy; var_Set( p_sys->p_input, "y-start", val );
            val.i_int = hl.ey; var_Set( p_sys->p_input, "y-end", val );

958 959
            val.p_address = (void *)p_sys->palette;
            var_Set( p_sys->p_input, "menu-palette", val );
Laurent Aimar's avatar
Laurent Aimar committed
960 961 962 963

            val.b_bool = VLC_TRUE; var_Set( p_sys->p_input, "highlight", val );
            vlc_mutex_unlock( p_mutex );

hartman's avatar
hartman committed
964
            msg_Dbg( p_demux, "buttonUpdate %d", i_button );
Laurent Aimar's avatar
Laurent Aimar committed
965 966 967
        }
        else
        {
968 969
            msg_Dbg( p_demux, "buttonUpdate not done b=%d t=%d",
                     i_button, i_title );
Laurent Aimar's avatar
Laurent Aimar committed
970 971 972

            /* Show all */
            vlc_mutex_lock( p_mutex );