bluray.c 79.2 KB
Newer Older
1 2 3
/*****************************************************************************
 * bluray.c: Blu-ray disc support plugin
 *****************************************************************************
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
4
 * Copyright © 2010-2012 VideoLAN, VLC authors and libbluray AUTHORS
5 6
 *
 * Authors: Jean-Baptiste Kempf <jb@videolan.org>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
7
 *          Hugo Beauzée-Luyssen <hugo@videolan.org>
8
 *
9 10 11
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
12 13 14 15
 * (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
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Lesser General Public License for more details.
18
 *
19 20 21
 * You should have received a copy of the GNU Lesser 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 28
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <assert.h>
29
#include <stdatomic.h>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
30

31
#if defined (HAVE_MNTENT_H) && defined(HAVE_SYS_STAT_H)
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
32
# include <mntent.h>
33
#endif
34 35
#include <fcntl.h>      /* O_* */
#include <unistd.h>     /* close() */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
36
#include <sys/stat.h>
37

38
#ifdef __APPLE__
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
39 40 41
# include <sys/param.h>
# include <sys/ucred.h>
# include <sys/mount.h>
42 43
#endif

44 45
#include <vlc_common.h>
#include <vlc_plugin.h>
46 47 48
#include <vlc_demux.h>                      /* demux_t */
#include <vlc_input.h>                      /* Seekpoints, chapters */
#include <vlc_dialog.h>                     /* BD+/AACS warnings */
49
#include <vlc_vout.h>                       /* vout_PutSubpicture / subpicture_t */
50
#include <vlc_url.h>                        /* vlc_path2uri */
51
#include <vlc_iso_lang.h>
52
#include <vlc_fs.h>
53 54 55 56

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

57 58

#include <libbluray/bluray.h>
59
#include <libbluray/bluray-version.h>
60
#include <libbluray/keys.h>
61
#include <libbluray/meta_data.h>
62
#include <libbluray/overlay.h>
63 64 65 66 67

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/

Rafaël Carré's avatar
Rafaël Carré committed
68 69 70 71 72
#define BD_MENU_TEXT        N_("Blu-ray menus")
#define BD_MENU_LONGTEXT    N_("Use Blu-ray menus. If disabled, "\
                                "the movie will start directly")
#define BD_REGION_TEXT      N_("Region code")
#define BD_REGION_LONGTEXT  N_("Blu-Ray player region code. "\
73 74 75 76 77 78 79 80
                                "Some discs can be played only with a correct region code.")

static const char *const ppsz_region_code[] = {
    "A", "B", "C" };
static const char *const ppsz_region_code_text[] = {
    "Region A", "Region B", "Region C" };

#define REGION_DEFAULT   1   /* Index to region list. Actual region code is (1<<REGION_DEFAULT) */
81
#define LANGUAGE_DEFAULT ("eng")
82

83 84 85 86
#if BLURAY_VERSION >= BLURAY_VERSION_CODE(0,8,0)
# define BLURAY_DEMUX
#endif

87 88 89 90
#ifndef BD_STREAM_TYPE_VIDEO_HEVC
# define BD_STREAM_TYPE_VIDEO_HEVC 0x24
#endif

91
/* Callbacks */
Rafaël Carré's avatar
Rafaël Carré committed
92 93
static int  blurayOpen (vlc_object_t *);
static void blurayClose(vlc_object_t *);
94 95

vlc_module_begin ()
Rafaël Carré's avatar
Rafaël Carré committed
96 97
    set_shortname(N_("Blu-ray"))
    set_description(N_("Blu-ray Disc support (libbluray)"))
98

Rafaël Carré's avatar
Rafaël Carré committed
99 100
    set_category(CAT_INPUT)
    set_subcategory(SUBCAT_INPUT_ACCESS)
101
    set_capability("access", 500)
102
    add_bool("bluray-menu", true, BD_MENU_TEXT, BD_MENU_LONGTEXT, false)
Rafaël Carré's avatar
Rafaël Carré committed
103 104
    add_string("bluray-region", ppsz_region_code[REGION_DEFAULT], BD_REGION_TEXT, BD_REGION_LONGTEXT, false)
        change_string_list(ppsz_region_code, ppsz_region_code_text)
105

Rafaël Carré's avatar
Rafaël Carré committed
106
    add_shortcut("bluray", "file")
107

Rafaël Carré's avatar
Rafaël Carré committed
108
    set_callbacks(blurayOpen, blurayClose)
109

110
#ifdef BLURAY_DEMUX
111 112 113 114 115 116 117
    /* demux module */
    add_submodule()
        set_description( "BluRay demuxer" )
        set_category( CAT_INPUT )
        set_subcategory( SUBCAT_INPUT_DEMUX )
        set_capability( "demux", 5 )
        set_callbacks( blurayOpen, blurayClose )
118
#endif
119

120 121
vlc_module_end ()

122 123
/* libbluray's overlay.h defines 2 types of overlay (bd_overlay_plane_e). */
#define MAX_OVERLAY 2
124

125 126 127 128 129 130 131 132
typedef enum OverlayStatus {
    Closed = 0,
    ToDisplay,  //Used to mark the overlay to be displayed the first time.
    Displayed,
    Outdated    //used to update the overlay after it has been sent to the vout
} OverlayStatus;

typedef struct bluray_overlay_t
133
{
134
    vlc_mutex_t         lock;
135
    int                 i_channel;
136 137
    OverlayStatus       status;
    subpicture_region_t *p_regions;
138
    int                 width, height;
139 140 141 142 143 144 145

    /* pointer to last subpicture updater.
     * used to disconnect this overlay from vout when:
     * - the overlay is closed
     * - vout is changed and this overlay is sent to the new vout
     */
    struct subpicture_updater_sys_t *p_updater;
146 147
} bluray_overlay_t;

148
typedef struct
149 150
{
    BLURAY              *bluray;
151 152

    /* Titles */
153 154 155
    unsigned int        i_title;
    unsigned int        i_longest_title;
    input_title_t       **pp_title;
156 157
    unsigned            cur_title;
    unsigned            cur_seekpoint;
158
    unsigned            updates;
159

160 161 162 163
    vlc_mutex_t             pl_info_lock;
    BLURAY_TITLE_INFO      *p_pl_info;
    const BLURAY_CLIP_INFO *p_clip_info;

164 165 166 167 168
    /* Attachments */
    int                 i_attachments;
    input_attachment_t  **attachments;
    int                 i_cover_idx;

169
    /* Meta information */
170 171
    const META_DL       *p_meta;

172
    /* Menus */
173
    bluray_overlay_t    *p_overlays[MAX_OVERLAY];
174
    bool                b_fatal_error;
175
    bool                b_menu;
Petri Hintukainen's avatar
Petri Hintukainen committed
176 177
    bool                b_menu_open;
    bool                b_popup_available;
178
    mtime_t             i_still_end_time;
179

180 181
    vlc_mutex_t         bdj_overlay_lock; /* used to lock BD-J overlay open/close while overlays are being sent to vout */

182 183
    /* */
    vout_thread_t       *p_vout;
184

185 186
    es_out_id_t         *p_dummy_video;

187
    /* TS stream */
188
    es_out_t            *p_out;
189
    vlc_array_t         es;
190 191
    int                 i_audio_stream_idx; /* Selected audio stream. -1 if default */
    int                 i_spu_stream_idx;   /* Selected subtitle stream. -1 if default */
192
    bool                b_spu_enable;       /* enabled / disabled */
193
    int                 i_video_stream;
194
    vlc_demux_chained_t *p_parser;
195
    bool                b_flushed;
196
    bool                b_pl_playing;       /* true when playing playlist */
197

198 199 200
    /* stream input */
    vlc_mutex_t         read_block_lock;

201 202
    /* Used to store bluray disc path */
    char                *psz_bd_path;
203
} demux_sys_t;
204 205 206

struct subpicture_updater_sys_t
{
207 208 209
    vlc_mutex_t          lock;      // protect p_overlay pointer and ref_cnt
    bluray_overlay_t    *p_overlay; // NULL if overlay has been closed
    int                  ref_cnt;   // one reference in vout (subpicture_t), one in input (bluray_overlay_t)
210 211
};

212 213 214 215 216 217 218 219
/*
 * cut the connection between vout and overlay.
 * - called when vout is closed or overlay is closed.
 * - frees subpicture_updater_sys_t when both sides have been closed.
 */
static void unref_subpicture_updater(subpicture_updater_sys_t *p_sys)
{
    vlc_mutex_lock(&p_sys->lock);
Hannes Domani's avatar
Hannes Domani committed
220
    int refs = --p_sys->ref_cnt;
221 222 223 224 225 226 227 228 229
    p_sys->p_overlay = NULL;
    vlc_mutex_unlock(&p_sys->lock);

    if (refs < 1) {
        vlc_mutex_destroy(&p_sys->lock);
        free(p_sys);
    }
}

230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
/* Get a 3 char code
 * FIXME: partiallyy duplicated from src/input/es_out.c
 */
static const char *DemuxGetLanguageCode( demux_t *p_demux, const char *psz_var )
{
    const iso639_lang_t *pl;
    char *psz_lang;
    char *p;

    psz_lang = var_CreateGetString( p_demux, psz_var );
    if( !psz_lang )
        return LANGUAGE_DEFAULT;

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

    for( pl = p_languages; pl->psz_eng_name != NULL; pl++ )
    {
        if( *psz_lang == '\0' )
            continue;
        if( !strcasecmp( pl->psz_eng_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_eng_name != NULL )
        return pl->psz_iso639_2T;

    return LANGUAGE_DEFAULT;
}

267 268 269
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
270
static es_out_t *esOutNew(demux_t *p_demux);
271

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
272 273
static int   blurayControl(demux_t *, int, va_list);
static int   blurayDemux(demux_t *);
274

275
static void  blurayInitTitles(demux_t *p_demux, uint32_t menu_titles);
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
276
static int   bluraySetTitle(demux_t *p_demux, int i_title);
277

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
278
static void  blurayOverlayProc(void *ptr, const BD_OVERLAY * const overlay);
279
static void  blurayArgbOverlayProc(void *ptr, const BD_ARGB_OVERLAY * const overlay);
280

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
281 282
static int   onMouseEvent(vlc_object_t *p_vout, const char *psz_var,
                          vlc_value_t old, vlc_value_t val, void *p_data);
283 284
static int   onIntfEvent(vlc_object_t *, char const *,
                         vlc_value_t, vlc_value_t, void *);
285

286
static void  blurayResetParser(demux_t *p_demux);
287
static void  notifyDiscontinuity( demux_sys_t *p_sys );
288

Petri Hintukainen's avatar
Petri Hintukainen committed
289 290
#define FROM_TICKS(a) ((a)*CLOCK_FREQ / INT64_C(90000))
#define TO_TICKS(a)   ((a)*INT64_C(90000)/CLOCK_FREQ)
291 292
#define CURRENT_TITLE p_sys->pp_title[p_sys->cur_title]
#define CUR_LENGTH    CURRENT_TITLE->i_length
293

294 295 296 297 298
/* */
static void FindMountPoint(char **file)
{
    char *device = *file;
#if defined (HAVE_MNTENT_H) && defined (HAVE_SYS_STAT_H)
299 300 301 302 303 304
    /* bd path may be a symlink (e.g. /dev/dvd -> /dev/sr0), so make sure
     * we look up the real device */
    char *bd_device = realpath(device, NULL);
    if (bd_device == NULL)
        return;

305
    struct stat st;
306
    if (lstat (bd_device, &st) == 0 && S_ISBLK (st.st_mode)) {
307
        FILE *mtab = setmntent ("/proc/self/mounts", "r");
308 309 310 311 312 313 314 315 316 317
        if (mtab) {
            struct mntent *m, mbuf;
            char buf [8192];

            while ((m = getmntent_r (mtab, &mbuf, buf, sizeof(buf))) != NULL) {
                if (!strcmp (m->mnt_fsname, bd_device)) {
                    free(device);
                    *file = strdup(m->mnt_dir);
                    break;
                }
318
            }
319
            endmntent (mtab);
320 321
        }
    }
322 323
    free(bd_device);

324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
#elif defined(__APPLE__)
    struct stat st;
    if (!stat (device, &st) && S_ISBLK (st.st_mode)) {
        int fs_count = getfsstat (NULL, 0, MNT_NOWAIT);
        if (fs_count > 0) {
            struct statfs mbuf[128];
            getfsstat (mbuf, fs_count * sizeof(mbuf[0]), MNT_NOWAIT);
            for (int i = 0; i < fs_count; ++i)
                if (!strcmp (mbuf[i].f_mntfromname, device)) {
                    free(device);
                    *file = strdup(mbuf[i].f_mntonname);
                    return;
                }
        }
    }
#else
# warning Disc device to mount point not implemented
341
    VLC_UNUSED( device );
342 343 344
#endif
}

345 346 347 348 349 350 351
static void blurayReleaseVout(demux_t *p_demux)
{
    demux_sys_t *p_sys = p_demux->p_sys;

    if (p_sys->p_vout != NULL) {
        var_DelCallback(p_sys->p_vout, "mouse-moved", onMouseEvent, p_demux);
        var_DelCallback(p_sys->p_vout, "mouse-clicked", onMouseEvent, p_demux);
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371

        for (int i = 0; i < MAX_OVERLAY; i++) {
            bluray_overlay_t *p_ov = p_sys->p_overlays[i];
            if (p_ov) {
                vlc_mutex_lock(&p_ov->lock);
                if (p_ov->i_channel != -1) {
                    msg_Err(p_demux, "blurayReleaseVout: subpicture channel exists\n");
                    vout_FlushSubpictureChannel(p_sys->p_vout, p_ov->i_channel);
                }
                p_ov->i_channel = -1;
                p_ov->status = ToDisplay;
                vlc_mutex_unlock(&p_ov->lock);

                if (p_ov->p_updater) {
                    unref_subpicture_updater(p_ov->p_updater);
                    p_ov->p_updater = NULL;
                }
            }
        }

372 373 374 375 376
        vlc_object_release(p_sys->p_vout);
        p_sys->p_vout = NULL;
    }
}

377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
/*****************************************************************************
 * BD-J background video
 *****************************************************************************/

static void startBackground(demux_t *p_demux)
{
    demux_sys_t *p_sys = p_demux->p_sys;

    if (p_sys->p_dummy_video) {
        return;
    }

    msg_Info(p_demux, "Start background");

    /* */
    es_format_t fmt;
    es_format_Init( &fmt, VIDEO_ES, VLC_CODEC_I420 );
    video_format_Setup( &fmt.video, VLC_CODEC_I420,
                        1920, 1080, 1920, 1080, 1, 1);
    fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN;
    fmt.i_id = 4115; /* 4113 = main video. 4114 = MVC. 4115 = unused. */
    fmt.i_group = 1;

    p_sys->p_dummy_video = es_out_Add(p_demux->out, &fmt);

    if (!p_sys->p_dummy_video) {
Petri Hintukainen's avatar
Petri Hintukainen committed
403 404
        msg_Err(p_demux, "Error adding background ES");
        goto out;
405 406
    }

407 408
    block_t *p_block = block_Alloc(fmt.video.i_width * fmt.video.i_height *
                                   fmt.video.i_bits_per_pixel / 8);
409 410
    if (!p_block) {
        msg_Err(p_demux, "Error allocating block for background video");
Petri Hintukainen's avatar
Petri Hintukainen committed
411
        goto out;
412 413 414 415 416 417
    }

    // XXX TODO: what would be correct timestamp ???
    p_block->i_dts = p_block->i_pts = mdate() + CLOCK_FREQ/25;

    uint8_t *p = p_block->p_buffer;
418 419 420
    memset(p, 0, fmt.video.i_width * fmt.video.i_height);
    p += fmt.video.i_width * fmt.video.i_height;
    memset(p, 0x80, fmt.video.i_width * fmt.video.i_height / 2);
421 422

    es_out_Send(p_demux->out, p_sys->p_dummy_video, p_block);
Petri Hintukainen's avatar
Petri Hintukainen committed
423 424 425

 out:
    es_format_Clean(&fmt);
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
}

static void stopBackground(demux_t *p_demux)
{
    demux_sys_t *p_sys = p_demux->p_sys;

    if (!p_sys->p_dummy_video) {
        return;
    }

    msg_Info(p_demux, "Stop background");

    es_out_Del(p_demux->out, p_sys->p_dummy_video);
    p_sys->p_dummy_video = NULL;
}

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
/*****************************************************************************
 * cache current playlist (title) information
 *****************************************************************************/

static void setTitleInfo(demux_sys_t *p_sys, BLURAY_TITLE_INFO *info)
{
    vlc_mutex_lock(&p_sys->pl_info_lock);

    if (p_sys->p_pl_info) {
        bd_free_title_info(p_sys->p_pl_info);
    }
    p_sys->p_pl_info   = info;
    p_sys->p_clip_info = NULL;

    if (p_sys->p_pl_info && p_sys->p_pl_info->clip_count) {
        p_sys->p_clip_info = &p_sys->p_pl_info->clips[0];
    }

    vlc_mutex_unlock(&p_sys->pl_info_lock);
}

463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
/*****************************************************************************
 * create input attachment for thumbnail
 *****************************************************************************/

static void attachThumbnail(demux_t *p_demux)
{
    demux_sys_t *p_sys = p_demux->p_sys;

    if (!p_sys->p_meta)
        return;

#if BLURAY_VERSION >= BLURAY_VERSION_CODE(0,9,0)
    if (p_sys->p_meta->thumb_count > 0 && p_sys->p_meta->thumbnails) {
        int64_t size;
        void *data;
        if (bd_get_meta_file(p_sys->bluray, p_sys->p_meta->thumbnails[0].path, &data, &size) > 0) {
            char psz_name[64];
            input_attachment_t *p_attachment;

            snprintf(psz_name, sizeof(psz_name), "picture%d_%s", p_sys->i_attachments, p_sys->p_meta->thumbnails[0].path);

            p_attachment = vlc_input_attachment_New(psz_name, NULL, "Album art", data, size);
            if (p_attachment) {
                p_sys->i_cover_idx = p_sys->i_attachments;
                TAB_APPEND(p_sys->i_attachments, p_sys->attachments, p_attachment);
            }
        }
        free(data);
    }
#endif
}

495 496 497 498 499 500 501 502
/*****************************************************************************
 * stream input
 *****************************************************************************/

static int probeStream(demux_t *p_demux)
{
    /* input must be seekable */
    bool b_canseek = false;
503
    vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_canseek );
504 505 506 507 508 509 510
    if (!b_canseek) {
        return VLC_EGENERIC;
    }

    /* first sector(s) should be filled with zeros */
    size_t i_peek;
    const uint8_t *p_peek;
511
    i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 2048 );
512 513 514 515 516 517 518 519 520 521 522 523
    if( i_peek != 2048 ) {
        return VLC_EGENERIC;
    }
    while (i_peek > 0) {
        if (p_peek[ --i_peek ]) {
            return VLC_EGENERIC;
        }
    }

    return VLC_SUCCESS;
}

524
#ifdef BLURAY_DEMUX
525 526 527 528 529 530 531 532 533 534
static int blurayReadBlock(void *object, void *buf, int lba, int num_blocks)
{
    demux_t *p_demux = (demux_t*)object;
    demux_sys_t *p_sys = p_demux->p_sys;
    int result = -1;

    assert(p_demux->s != NULL);

    vlc_mutex_lock(&p_sys->read_block_lock);

535
    if (vlc_stream_Seek( p_demux->s, lba * INT64_C(2048) ) == VLC_SUCCESS) {
536 537 538
        size_t  req = (size_t)2048 * num_blocks;
        ssize_t got;

539
        got = vlc_stream_Read( p_demux->s, buf, req);
540 541 542 543 544 545 546 547 548 549 550 551 552
        if (got < 0) {
            msg_Err(p_demux, "read from lba %d failed", lba);
        } else {
            result = got / 2048;
        }
    } else {
       msg_Err(p_demux, "seek to lba %d failed", lba);
    }

    vlc_mutex_unlock(&p_sys->read_block_lock);

    return result;
}
553
#endif
554

555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
/*****************************************************************************
 * probing of local files
 *****************************************************************************/

/* Descriptor Tag (ECMA 167, 3/7.2) */
static int decode_descriptor_tag(const uint8_t *buf)
{
    uint16_t id;
    uint8_t  checksum = 0;
    int      i;

    id = buf[0] | (buf[1] << 8);

    /* calculate tag checksum */
    for (i = 0; i < 4; i++) {
        checksum = (uint8_t)(checksum + buf[i]);
    }
    for (i = 5; i < 16; i++) {
        checksum = (uint8_t)(checksum + buf[i]);
    }

    if (checksum != buf[4]) {
        return -1;
    }

    return id;
}

static int probeFile(const char *psz_name)
{
    struct stat stat_info;
    uint8_t peek[2048];
    unsigned i;
    int ret = VLC_EGENERIC;
    int fd;

    fd = vlc_open(psz_name, O_RDONLY | O_NONBLOCK);
    if (fd == -1) {
        return VLC_EGENERIC;
    }

    if (fstat(fd, &stat_info) == -1) {
        goto bailout;
    }
    if (!S_ISREG(stat_info.st_mode) && !S_ISBLK(stat_info.st_mode)) {
        goto bailout;
    }

    /* first sector should be filled with zeros */
    if (read(fd, peek, sizeof(peek)) != sizeof(peek)) {
        goto bailout;
    }
    for (i = 0; i < sizeof(peek); i++) {
        if (peek[ i ]) {
            goto bailout;
        }
    }

    /* Check AVDP tag checksum */
    if (lseek(fd, 256 * 2048, SEEK_SET) == -1 ||
        read(fd, peek, 16) != 16 ||
        decode_descriptor_tag(peek) != 2) {
        goto bailout;
    }

    ret = VLC_SUCCESS;

bailout:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
623
    vlc_close(fd);
624 625 626
    return ret;
}

627 628 629
/*****************************************************************************
 * blurayOpen: module init function
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
630
static int blurayOpen(vlc_object_t *object)
631
{
632 633
    demux_t *p_demux = (demux_t*)object;
    demux_sys_t *p_sys;
634 635
    bool forced;
    uint64_t i_init_pos = 0;
636

637
    const char *error_msg = NULL;
638
#define BLURAY_ERROR(s) do { error_msg = s; goto error; } while(0)
639

640
    if (p_demux->out == NULL || unlikely(p_demux->p_input == NULL))
641 642
        return VLC_EGENERIC;

643
    forced = !strncasecmp(p_demux->psz_url, "bluray:", 7);
644 645

    if (p_demux->s) {
646
        if (!strncasecmp(p_demux->psz_url, "file:", 5)) {
647 648 649 650 651 652 653
            /* use access_demux for local files */
            return VLC_EGENERIC;
        }

        if (probeStream(p_demux) != VLC_SUCCESS) {
            return VLC_EGENERIC;
        }
654

Petri Hintukainen's avatar
Petri Hintukainen committed
655
    } else if (!forced) {
656
        if (!p_demux->psz_filepath) {
657 658 659
            return VLC_EGENERIC;
        }

660
        if (probeFile(p_demux->psz_filepath) != VLC_SUCCESS) {
661 662
            return VLC_EGENERIC;
        }
663 664
    }

665
    /* */
666
    p_demux->p_sys = p_sys = vlc_obj_calloc(object, 1, sizeof(*p_sys));
Rafaël Carré's avatar
Rafaël Carré committed
667
    if (unlikely(!p_sys))
668
        return VLC_ENOMEM;
Rafaël Carré's avatar
Rafaël Carré committed
669

670 671
    p_sys->i_audio_stream_idx = -1;
    p_sys->i_spu_stream_idx = -1;
672
    p_sys->i_video_stream = -1;
673
    p_sys->i_still_end_time = 0;
674 675

    /* init demux info fields */
676
    p_sys->updates = 0;
677

Rafaël Carré's avatar
Rafaël Carré committed
678
    TAB_INIT(p_sys->i_title, p_sys->pp_title);
679
    TAB_INIT(p_sys->i_attachments, p_sys->attachments);
680

681 682
    vlc_mutex_init(&p_sys->pl_info_lock);
    vlc_mutex_init(&p_sys->bdj_overlay_lock);
683 684
    vlc_mutex_init(&p_sys->read_block_lock); /* used during bd_open_stream() */

685 686 687 688 689
    /* request sub demuxers to skip continuity check as some split
       file concatenation are just resetting counters... */
    var_Create( p_demux, "ts-cc-check", VLC_VAR_BOOL );
    var_SetBool( p_demux, "ts-cc-check", false );

690 691
    var_AddCallback( p_demux->p_input, "intf-event", onIntfEvent, p_demux );

692
    /* Open BluRay */
693
#ifdef BLURAY_DEMUX
694
    if (p_demux->s) {
695
        i_init_pos = vlc_stream_Tell(p_demux->s);
696 697 698 699 700 701

        p_sys->bluray = bd_init();
        if (!bd_open_stream(p_sys->bluray, p_demux, blurayReadBlock)) {
            bd_close(p_sys->bluray);
            p_sys->bluray = NULL;
        }
702 703 704
    } else
#endif
    {
705
        if (!p_demux->psz_filepath) {
Petri Hintukainen's avatar
Petri Hintukainen committed
706 707 708 709
            /* no path provided (bluray://). use default DVD device. */
            p_sys->psz_bd_path = var_InheritString(object, "dvd");
        } else {
            /* store current bd path */
710
            p_sys->psz_bd_path = strdup(p_demux->psz_filepath);
Petri Hintukainen's avatar
Petri Hintukainen committed
711
        }
712 713 714 715 716 717

        /* If we're passed a block device, try to convert it to the mount point. */
        FindMountPoint(&p_sys->psz_bd_path);

        p_sys->bluray = bd_open(p_sys->psz_bd_path, NULL);
    }
718
    if (!p_sys->bluray) {
719
        goto error;
720 721
    }

722 723
    /* Warning the user about AACS/BD+ */
    const BLURAY_DISC_INFO *disc_info = bd_get_disc_info(p_sys->bluray);
724 725

    /* Is it a bluray? */
726 727 728 729 730 731
    if (!disc_info->bluray_detected) {
        if (forced) {
            BLURAY_ERROR(_("Path doesn't appear to be a Blu-ray"));
        }
        goto error;
    }
732

733 734
    msg_Info(p_demux, "First play: %i, Top menu: %i\n"
                      "HDMV Titles: %i, BD-J Titles: %i, Other: %i",
735 736 737 738 739 740
             disc_info->first_play_supported, disc_info->top_menu_supported,
             disc_info->num_hdmv_titles, disc_info->num_bdj_titles,
             disc_info->num_unsupported_titles);

    /* AACS */
    if (disc_info->aacs_detected) {
741
        msg_Dbg(p_demux, "Disc is using AACS");
742 743 744
        if (!disc_info->libaacs_detected)
            BLURAY_ERROR(_("This Blu-ray Disc needs a library for AACS decoding"
                      ", and your system does not have it."));
745
        if (!disc_info->aacs_handled) {
746 747
            if (disc_info->aacs_error_code) {
                switch (disc_info->aacs_error_code) {
748 749 750 751 752 753 754 755 756 757 758 759
                case BD_AACS_CORRUPTED_DISC:
                    BLURAY_ERROR(_("Blu-ray Disc is corrupted."));
                case BD_AACS_NO_CONFIG:
                    BLURAY_ERROR(_("Missing AACS configuration file!"));
                case BD_AACS_NO_PK:
                    BLURAY_ERROR(_("No valid processing key found in AACS config file."));
                case BD_AACS_NO_CERT:
                    BLURAY_ERROR(_("No valid host certificate found in AACS config file."));
                case BD_AACS_CERT_REVOKED:
                    BLURAY_ERROR(_("AACS Host certificate revoked."));
                case BD_AACS_MMC_FAILED:
                    BLURAY_ERROR(_("AACS MMC failed."));
760 761
                }
            }
762 763 764 765 766
        }
    }

    /* BD+ */
    if (disc_info->bdplus_detected) {
767
        msg_Dbg(p_demux, "Disc is using BD+");
768 769 770 771 772 773
        if (!disc_info->libbdplus_detected)
            BLURAY_ERROR(_("This Blu-ray Disc needs a library for BD+ decoding"
                      ", and your system does not have it."));
        if (!disc_info->bdplus_handled)
            BLURAY_ERROR(_("Your system BD+ decoding library does not work. "
                      "Missing configuration?"));
774 775
    }

776 777 778 779 780 781
    /* set player region code */
    char *psz_region = var_InheritString(p_demux, "bluray-region");
    unsigned int region = psz_region ? (psz_region[0] - 'A') : REGION_DEFAULT;
    free(psz_region);
    bd_set_player_setting(p_sys->bluray, BLURAY_PLAYER_SETTING_REGION_CODE, 1<<region);

782 783 784 785 786 787 788 789
    /* set preferred languages */
    const char *psz_code = DemuxGetLanguageCode( p_demux, "audio-language" );
    bd_set_player_setting_str(p_sys->bluray, BLURAY_PLAYER_SETTING_AUDIO_LANG, psz_code);
    psz_code = DemuxGetLanguageCode( p_demux, "sub-language" );
    bd_set_player_setting_str(p_sys->bluray, BLURAY_PLAYER_SETTING_PG_LANG,    psz_code);
    psz_code = DemuxGetLanguageCode( p_demux, "menu-language" );
    bd_set_player_setting_str(p_sys->bluray, BLURAY_PLAYER_SETTING_MENU_LANG,  psz_code);

Petri Hintukainen's avatar
Petri Hintukainen committed
790
    /* Get disc metadata */
791 792
    p_sys->p_meta = bd_get_meta(p_sys->bluray);
    if (!p_sys->p_meta)
Rafaël Carré's avatar
Rafaël Carré committed
793
        msg_Warn(p_demux, "Failed to get meta info.");
794

795 796 797
    p_sys->i_cover_idx = -1;
    attachThumbnail(p_demux);

798 799
    p_sys->b_menu = var_InheritBool(p_demux, "bluray-menu");

800 801 802 803 804
    /* Check BD-J capability */
    if (p_sys->b_menu && disc_info->bdj_detected && !disc_info->bdj_handled) {
        msg_Err(p_demux, "BD-J menus not supported. Playing without menus. "
                "BD-J support: %d, JVM found: %d, JVM usable: %d",
                disc_info->bdj_supported, disc_info->libjvm_detected, disc_info->bdj_handled);
Thomas Guillem's avatar
Thomas Guillem committed
805
        vlc_dialog_display_error(p_demux, _("Java required"),
806 807
             _("This Blu-ray disc requires Java for menus support.%s\nThe disc will be played without menus."),
             !disc_info->libjvm_detected ? _("Java was not found on your system.") : "");
808 809 810 811
        p_sys->b_menu = false;
    }

    /* Get titles and chapters */
812
    blurayInitTitles(p_demux, disc_info->num_hdmv_titles + disc_info->num_bdj_titles + 1/*Top Menu*/ + 1/*First Play*/);
813

814 815 816 817 818
    /*
     * Initialize the event queue, so we can receive events in blurayDemux(Menu).
     */
    bd_get_event(p_sys->bluray, NULL);

819 820 821
    /* Registering overlay event handler */
    bd_register_overlay_proc(p_sys->bluray, p_demux, blurayOverlayProc);

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
822
    if (p_sys->b_menu) {
823

824
        /* Register ARGB overlay handler for BD-J */
825
        if (disc_info->num_bdj_titles)
826 827
            bd_register_argb_overlay_proc(p_sys->bluray, p_demux, blurayArgbOverlayProc, NULL);

828
        /* libbluray will start playback from "First-Title" title */
829 830 831
        if (bd_play(p_sys->bluray) == 0)
            BLURAY_ERROR(_("Failed to start bluray playback. Please try without menu support."));

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
832
    } else {
833
        /* set start title number */
834
        if (bluraySetTitle(p_demux, p_sys->i_longest_title) != VLC_SUCCESS) {
Rafaël Carré's avatar
Rafaël Carré committed
835
            msg_Err(p_demux, "Could not set the title %d", p_sys->i_longest_title);
836 837
            goto error;
        }
838 839
    }

840
    vlc_array_init(&p_sys->es);
Rafaël Carré's avatar
Rafaël Carré committed
841
    p_sys->p_out = esOutNew(p_demux);
Rafaël Carré's avatar
Rafaël Carré committed
842
    if (unlikely(p_sys->p_out == NULL))
843 844
        goto error;

Rafaël Carré's avatar
Rafaël Carré committed
845
    blurayResetParser(p_demux);
846 847
    if (!p_sys->p_parser) {
        msg_Err(p_demux, "Failed to create TS demuxer");
848
        goto error;
849 850 851 852
    }

    p_demux->pf_control = blurayControl;
    p_demux->pf_demux   = blurayDemux;
853 854

    return VLC_SUCCESS;
855 856 857

error:
    if (error_msg)
Thomas Guillem's avatar
Thomas Guillem committed
858
        vlc_dialog_display_error(p_demux, _("Blu-ray error"), "%s", error_msg);
859
    blurayClose(object);
860 861 862

    if (p_demux->s != NULL) {
        /* restore stream position */
863
        if (vlc_stream_Seek(p_demux->s, i_init_pos) != VLC_SUCCESS) {
864 865 866 867 868
            msg_Err(p_demux, "Failed to seek back to stream start");
            return VLC_ETIMEOUT;
        }
    }

869
    return VLC_EGENERIC;
870
#undef BLURAY_ERROR
871 872 873 874 875 876
}


/*****************************************************************************
 * blurayClose: module destroy function
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
877
static void blurayClose(vlc_object_t *object)
878
{
879 880 881
    demux_t *p_demux = (demux_t*)object;
    demux_sys_t *p_sys = p_demux->p_sys;

882 883
    var_DelCallback( p_demux->p_input, "intf-event", onIntfEvent, p_demux );

884 885
    setTitleInfo(p_sys, NULL);

886 887 888
    /*
     * Close libbluray first.
     * This will close all the overlays before we release p_vout
Rafaël Carré's avatar
Rafaël Carré committed
889
     * bd_close(NULL) can crash
890
     */
891 892 893
    if (p_sys->bluray) {
        bd_close(p_sys->bluray);
    }
894

895 896
    blurayReleaseVout(p_demux);

897
    if (p_sys->p_parser)
898
        vlc_demux_chained_Delete(p_sys->p_parser);
899 900
    if (p_sys->p_out != NULL)
        es_out_Delete(p_sys->p_out);
Rafaël Carré's avatar
Rafaël Carré committed
901 902
    assert(vlc_array_count(&p_sys->es) == 0);
    vlc_array_clear(&p_sys->es);
903

904 905 906
    /* Titles */
    for (unsigned int i = 0; i < p_sys->i_title; i++)
        vlc_input_title_Delete(p_sys->pp_title[i]);
Rafaël Carré's avatar
Rafaël Carré committed
907
    TAB_CLEAN(p_sys->i_title, p_sys->pp_title);
908

909 910 911 912
    for (int i = 0; i < p_sys->i_attachments; i++)
      vlc_input_attachment_Delete(p_sys->attachments[i]);
    TAB_CLEAN(p_sys->i_attachments, p_sys->attachments);

913
    vlc_mutex_destroy(&p_sys->pl_info_lock);
914
    vlc_mutex_destroy(&p_sys->bdj_overlay_lock);
915
    vlc_mutex_destroy(&p_sys->read_block_lock);
916

917
    free(p_sys->psz_bd_path);
918 919
}

920 921 922 923
/*****************************************************************************
 * Elementary streams handling
 *****************************************************************************/

924 925
typedef struct
{
926
    demux_t *p_demux;
927
} es_out_sys_t;
928

929 930 931 932 933
typedef struct  fmt_es_pair {
    int         i_id;
    es_out_id_t *p_es;
}               fmt_es_pair_t;

Rafaël Carré's avatar
Rafaël Carré committed
934
static int  findEsPairIndex(demux_sys_t *p_sys, int i_id)
935
{
936
    for (size_t i = 0; i < vlc_array_count(&p_sys->es); ++i)
Rafaël Carré's avatar
Rafaël Carré committed
937
        if (((fmt_es_pair_t*)vlc_array_item_at_index(&p_sys->es, i))->i_id == i_id)
938
            return i;
Rafaël Carré's avatar
Rafaël Carré committed
939

940 941 942
    return -1;
}

Rafaël Carré's avatar
Rafaël Carré committed
943
static int  findEsPairIndexByEs(demux_sys_t *p_sys, es_out_id_t *p_es)
944
{
945
    for (size_t i = 0; i < vlc_array_count(&p_sys->es); ++i)
Rafaël Carré's avatar
Rafaël Carré committed
946
        if (((fmt_es_pair_t*)vlc_array_item_at_index(&p_sys->es, i))->p_es == p_es)
947
            return i;
Rafaël Carré's avatar
Rafaël Carré committed
948

949 950 951
    return -1;
}

952
static void setStreamLang(demux_sys_t *p_sys, es_format_t *p_fmt)
953
{
954 955 956 957 958 959 960 961 962 963 964 965 966 967 968
    const BLURAY_STREAM_INFO *p_streams;
    int i_stream_count = 0;

    vlc_mutex_lock(&p_sys->pl_info_lock);

    if (p_sys->p_clip_info) {
        if (p_fmt->i_cat == AUDIO_ES) {
            p_streams      = p_sys->p_clip_info->audio_streams;
            i_stream_count = p_sys->p_clip_info->audio_stream_count;
        } else if (p_fmt->i_cat == SPU_ES) {
            p_streams      = p_sys->p_clip_info->pg_streams;
            i_stream_count = p_sys->p_clip_info->pg_stream_count;
        }
    }

969 970 971
    for (int i = 0; i < i_stream_count; i++) {
        if (p_fmt->i_id == p_streams[i].pid) {
            free(p_fmt->psz_language);
972
            p_fmt->psz_language = strndup((const char *)p_streams[i].lang, 3);
973
            break;
974 975
        }
    }
976 977 978 979 980 981 982 983 984 985 986 987

    vlc_mutex_unlock(&p_sys->pl_info_lock);
}

static int blurayEsPid(demux_sys_t *p_sys, int es_type, int i_es_idx)
{
    int i_pid = -1;

    vlc_mutex_lock(&p_sys->pl_info_lock);

    if (p_sys->p_clip_info) {
        if (es_type == AUDIO_ES) {
988
            if (i_es_idx >= 0 && i_es_idx < p_sys->p_clip_info->audio_stream_count) {
989 990 991 992 993 994 995 996 997 998 999 1000
                i_pid = p_sys->p_clip_info->audio_streams[i_es_idx].pid;
            }
        } else if (es_type == SPU_ES) {
            if (i_es_idx >= 0 && i_es_idx < p_sys->p_clip_info->pg_stream_count) {
                i_pid = p_sys->p_clip_info->pg_streams[i_es_idx].pid;
            }
        }
    }

    vlc_mutex_unlock(&p_sys->pl_info_lock);

    return i_pid;
1001 1002
}

Rafaël Carré's avatar
Rafaël Carré committed
1003
static es_out_id_t *esOutAdd(es_out_t *p_out, const es_format_t *p_fmt)
1004
{
1005 1006
    es_out_sys_t *es_out_sys = p_out->p_sys;
    demux_t *p_demux = es_out_sys->p_demux;
1007
    demux_sys_t *p_sys = p_demux->p_sys;
1008
    es_format_t fmt;
1009
    bool b_select = false;
1010 1011

    es_format_Copy(&fmt, p_fmt);
1012

Rafaël Carré's avatar
Rafaël Carré committed
1013
    switch (fmt.i_cat) {
1014
    case VIDEO_ES:
Rafaël Carré's avatar
Rafaël Carré committed
1015
        if (p_sys->i_video_stream != -1 && p_sys->i_video_stream != p_fmt->i_id)
1016
            fmt.i_priority = ES_PRIORITY_NOT_SELECTABLE;
1017 1018
        break ;
    case AUDIO_ES:
1019 1020 1021
        if (p_sys->i_audio_stream_idx != -1) {
            if (blurayEsPid(p_sys, AUDIO_ES, p_sys->i_audio_stream_idx) == p_fmt->i_id)
                b_select = true;
1022
            fmt.i_priority = ES_PRIORITY_NOT_SELECTABLE;
1023
        }
1024
        setStreamLang(p_sys, &fmt);
1025 1026
        break ;
    case SPU_ES:
1027 1028 1029
        if (p_sys->i_spu_stream_idx != -1) {
            if (blurayEsPid(p_sys, SPU_ES, p_sys->i_spu_stream_idx) == p_fmt->i_id)
                b_select = true;
1030
            fmt.i_priority = ES_PRIORITY_NOT_SELECTABLE;
1031
        }
1032
        setStreamLang(p_sys, &fmt);
1033
        break ;
1034 1035
    default:
        ;
1036 1037
    }

1038
    es_out_id_t *p_es = es_out_Add(p_demux->out, &fmt);
Rafaël Carré's avatar
Rafaël Carré committed
1039
    if (p_fmt->i_id >= 0) {
1040
        /* Ensure we are not overriding anything */
1041
        int idx = findEsPairIndex(p_sys, p_fmt->i_id);
Rafaël Carré's avatar
Rafaël Carré committed
1042 1043 1044
        if (idx == -1) {
            fmt_es_pair_t *p_pair = malloc(sizeof(*p_pair));
            if (likely(p_pair != NULL)) {
1045 1046
                p_pair->i_id = p_fmt->i_id;
                p_pair->p_es = p_es;
1047
                msg_Info(p_demux, "Adding ES %d", p_fmt->i_id);
1048
                vlc_array_append_or_abort(&p_sys->es, p_pair);
1049 1050 1051 1052 1053 1054 1055 1056

                if (b_select) {
                    if (fmt.i_cat == AUDIO_ES) {
                        var_SetInteger( p_demux->p_input, "audio-es", p_fmt->i_id );
                    } else if (fmt.i_cat == SPU_ES) {
                        var_SetInteger( p_demux->p_input, "spu-es", p_sys->b_spu_enable ? p_fmt->i_id : -1 );
                    }
                }
1057 1058 1059
            }
        }
    }
1060
    es_format_Clean(&fmt);
1061
    return p_es;
1062 1063
}

Rafaël Carré's avatar
Rafaël Carré committed
1064
static int esOutSend(es_out_t *p_out, es_out_id_t *p_es, block_t *p_block)